changeset 718:e5f4385615ac tony_integration

Merge from tonioni branch
author Chris Cannam
date Tue, 28 Jan 2014 15:02:09 +0000
parents b81f21f2c4c3 (current diff) 137d3ff48f73 (diff)
children a352fb986e7b
files layer/layer.pro view/view.pro widgets/widgets.pro
diffstat 23 files changed, 2510 insertions(+), 470 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/FlexiNoteLayer.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -0,0 +1,1702 @@
+/* -*- 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 file copyright 2006 Chris Cannam.
+    
+    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.
+*/
+
+#include "FlexiNoteLayer.h"
+
+#include "data/model/Model.h"
+#include "data/model/SparseTimeValueModel.h"
+#include "base/RealTime.h"
+#include "base/Profiler.h"
+#include "base/Pitch.h"
+#include "base/LogRange.h"
+#include "base/RangeMapper.h"
+#include "ColourDatabase.h"
+#include "view/View.h"
+
+#include "PianoScale.h"
+#include "LinearNumericalScale.h"
+#include "LogNumericalScale.h"
+
+#include "data/model/FlexiNoteModel.h"
+
+#include "widgets/ItemEditDialog.h"
+#include "widgets/TextAbbrev.h"
+
+#include <QPainter>
+#include <QPainterPath>
+#include <QMouseEvent>
+#include <QTextStream>
+#include <QMessageBox>
+
+#include <iostream>
+#include <cmath>
+#include <utility>
+#include <limits> // GF: included to compile std::numerical_limits on linux
+#include <vector>
+
+
+FlexiNoteLayer::FlexiNoteLayer() :
+    SingleColourLayer(),
+
+    // m_model(0),
+    // m_editing(false),
+    // m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
+    // m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
+    // m_editingCommand(0),
+    // m_verticalScale(AutoAlignScale),
+    // m_scaleMinimum(0),
+    // m_scaleMaximum(0)
+
+    m_model(0),
+    m_editing(false),
+    m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
+    m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
+    m_editingCommand(0),
+    m_verticalScale(AutoAlignScale),
+    m_editMode(DragNote),
+    m_scaleMinimum(34), 
+    m_scaleMaximum(77),
+    m_intelligentActions(true)
+{
+}
+
+void
+FlexiNoteLayer::setModel(FlexiNoteModel *model) 
+{
+    if (m_model == model) return;
+    m_model = model;
+
+    connectSignals(m_model);
+
+    // m_scaleMinimum = 0;
+    // m_scaleMaximum = 0;
+
+    emit modelReplaced();
+}
+
+Layer::PropertyList
+FlexiNoteLayer::getProperties() const
+{
+    PropertyList list = SingleColourLayer::getProperties();
+    list.push_back("Vertical Scale");
+    list.push_back("Scale Units");
+    return list;
+}
+
+QString
+FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const
+{
+    if (name == "Vertical Scale") return tr("Vertical Scale");
+    if (name == "Scale Units") return tr("Scale Units");
+    return SingleColourLayer::getPropertyLabel(name);
+}
+
+Layer::PropertyType
+FlexiNoteLayer::getPropertyType(const PropertyName &name) const
+{
+    if (name == "Scale Units") return UnitsProperty;
+    if (name == "Vertical Scale") return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
+}
+
+QString
+FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const
+{
+    if (name == "Vertical Scale" || name == "Scale Units") {
+        return tr("Scale");
+    }
+    return SingleColourLayer::getPropertyGroupName(name);
+}
+
+QString
+FlexiNoteLayer::getScaleUnits() const
+{
+    if (m_model) return m_model->getScaleUnits();
+    else return "";
+}
+
+int
+FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name,
+                                         int *min, int *max, int *deflt) const
+{
+    int val = 0;
+
+    if (name == "Vertical Scale") {
+    
+        if (min) *min = 0;
+        if (max) *max = 3;
+        if (deflt) *deflt = int(AutoAlignScale);
+    
+        val = int(m_verticalScale);
+
+    } else if (name == "Scale Units") {
+
+        if (deflt) *deflt = 0;
+        if (m_model) {
+            val = UnitDatabase::getInstance()->getUnitId
+                (getScaleUnits());
+        }
+
+    } else {
+
+        val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
+    }
+
+    return val;
+}
+
+QString
+FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name,
+                                      int value) const
+{
+    if (name == "Vertical Scale") {
+        switch (value) {
+        default:
+        case 0: return tr("Auto-Align");
+        case 1: return tr("Linear");
+        case 2: return tr("Log");
+        case 3: return tr("MIDI Notes");
+        }
+    }
+    return SingleColourLayer::getPropertyValueLabel(name, value);
+}
+
+void
+FlexiNoteLayer::setProperty(const PropertyName &name, int value)
+{
+    if (name == "Vertical Scale") {
+        setVerticalScale(VerticalScale(value));
+    } else if (name == "Scale Units") {
+        if (m_model) {
+            m_model->setScaleUnits
+                (UnitDatabase::getInstance()->getUnitById(value));
+            emit modelChanged();
+        }
+    } else {
+        return SingleColourLayer::setProperty(name, value);
+    }
+}
+
+void
+FlexiNoteLayer::setVerticalScale(VerticalScale scale)
+{
+    if (m_verticalScale == scale) return;
+    m_verticalScale = scale;
+    emit layerParametersChanged();
+}
+
+bool
+FlexiNoteLayer::isLayerScrollable(const View *v) const
+{
+    QPoint discard;
+    return !v->shouldIlluminateLocalFeatures(this, discard);
+}
+
+bool
+FlexiNoteLayer::shouldConvertMIDIToHz() const
+{
+    QString unit = getScaleUnits();
+    return (unit != "Hz");
+//    if (unit == "" ||
+//        unit.startsWith("MIDI") ||
+//        unit.startsWith("midi")) return true;
+//    return false;
+}
+
+bool
+FlexiNoteLayer::getValueExtents(float &min, float &max,
+                                bool &logarithmic, QString &unit) const
+{
+    if (!m_model) return false;
+    min = m_model->getValueMinimum();
+    max = m_model->getValueMaximum();
+
+    if (shouldConvertMIDIToHz()) {
+        unit = "Hz";
+        min = Pitch::getFrequencyForPitch(lrintf(min));
+        max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+    } else unit = getScaleUnits();
+
+    if (m_verticalScale == MIDIRangeScale ||
+        m_verticalScale == LogScale) logarithmic = true;
+
+    return true;
+}
+
+bool
+FlexiNoteLayer::getDisplayExtents(float &min, float &max) const
+{
+    if (!m_model || shouldAutoAlign()) {
+//        std::cerr << "No model or shouldAutoAlign()" << std::endl;
+        return false;
+    }
+
+    if (m_verticalScale == MIDIRangeScale) {
+        min = Pitch::getFrequencyForPitch(0);
+        max = Pitch::getFrequencyForPitch(127);
+        return true;
+    }
+
+    if (m_scaleMinimum == m_scaleMaximum) {
+        min = m_model->getValueMinimum();
+        max = m_model->getValueMaximum();
+    } else {
+        min = m_scaleMinimum;
+        max = m_scaleMaximum;
+    }
+
+    if (shouldConvertMIDIToHz()) {
+        min = Pitch::getFrequencyForPitch(lrintf(min));
+        max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+    }
+
+#ifdef DEBUG_NOTE_LAYER
+    cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl;
+#endif
+
+    return true;
+}
+
+bool
+FlexiNoteLayer::setDisplayExtents(float min, float max)
+{
+    if (!m_model) return false;
+
+    if (min == max) {
+        if (min == 0.f) {
+            max = 1.f;
+        } else {
+            max = min * 1.0001;
+        }
+    }
+
+    m_scaleMinimum = min;
+    m_scaleMaximum = max;
+
+#ifdef DEBUG_NOTE_LAYER
+    cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
+#endif
+    
+    emit layerParametersChanged();
+    return true;
+}
+
+int
+FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const
+{
+    if (shouldAutoAlign()) return 0;
+    if (!m_model) return 0;
+
+    defaultStep = 0;
+    return 100;
+}
+
+int
+FlexiNoteLayer::getCurrentVerticalZoomStep() const
+{
+    if (shouldAutoAlign()) return 0;
+    if (!m_model) return 0;
+
+    RangeMapper *mapper = getNewVerticalZoomRangeMapper();
+    if (!mapper) return 0;
+
+    float dmin, dmax;
+    getDisplayExtents(dmin, dmax);
+
+    int nr = mapper->getPositionForValue(dmax - dmin);
+
+    delete mapper;
+
+    return 100 - nr;
+}
+
+//!!! lots of duplication with TimeValueLayer
+
+void
+FlexiNoteLayer::setVerticalZoomStep(int step)
+{
+    if (shouldAutoAlign()) return;
+    if (!m_model) return;
+
+    RangeMapper *mapper = getNewVerticalZoomRangeMapper();
+    if (!mapper) return;
+    
+    float min, max;
+    bool logarithmic;
+    QString unit;
+    getValueExtents(min, max, logarithmic, unit);
+    
+    float dmin, dmax;
+    getDisplayExtents(dmin, dmax);
+
+    float newdist = mapper->getValueForPosition(100 - step);
+
+    float newmin, newmax;
+
+    if (logarithmic) {
+
+        // see SpectrogramLayer::setVerticalZoomStep
+
+        newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
+        newmin = newmax - newdist;
+
+//        cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
+
+    } else {
+        float dmid = (dmax + dmin) / 2;
+        newmin = dmid - newdist / 2;
+        newmax = dmid + newdist / 2;
+    }
+
+    if (newmin < min) {
+        newmax += (min - newmin);
+        newmin = min;
+    }
+    if (newmax > max) {
+        newmax = max;
+    }
+    
+#ifdef DEBUG_NOTE_LAYER
+    cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
+#endif
+
+    setDisplayExtents(newmin, newmax);
+}
+
+RangeMapper *
+FlexiNoteLayer::getNewVerticalZoomRangeMapper() const
+{
+    if (!m_model) return 0;
+    
+    RangeMapper *mapper;
+
+    float min, max;
+    bool logarithmic;
+    QString unit;
+    getValueExtents(min, max, logarithmic, unit);
+
+    if (min == max) return 0;
+    
+    if (logarithmic) {
+        mapper = new LogRangeMapper(0, 100, min, max, unit);
+    } else {
+        mapper = new LinearRangeMapper(0, 100, min, max, unit);
+    }
+
+    return mapper;
+}
+
+FlexiNoteModel::PointList
+FlexiNoteLayer::getLocalPoints(View *v, int x) const
+{
+    if (!m_model) return FlexiNoteModel::PointList();
+
+    long frame = v->getFrameForX(x);
+
+    FlexiNoteModel::PointList onPoints =
+        m_model->getPoints(frame);
+
+    if (!onPoints.empty()) {
+        return onPoints;
+    }
+
+    FlexiNoteModel::PointList prevPoints =
+        m_model->getPreviousPoints(frame);
+    FlexiNoteModel::PointList nextPoints =
+        m_model->getNextPoints(frame);
+
+    FlexiNoteModel::PointList usePoints = prevPoints;
+
+    if (prevPoints.empty()) {
+        usePoints = nextPoints;
+    } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
+               !(nextPoints.begin()->frame > v->getEndFrame())) {
+        usePoints = nextPoints;
+    } else if (long(nextPoints.begin()->frame) - frame <
+               frame - long(prevPoints.begin()->frame)) {
+        usePoints = nextPoints;
+    }
+
+    if (!usePoints.empty()) {
+        int fuzz = 2;
+        int px = v->getXForFrame(usePoints.begin()->frame);
+        if ((px > x && px - x > fuzz) ||
+            (px < x && x - px > fuzz + 1)) {
+            usePoints.clear();
+        }
+    }
+
+    return usePoints;
+}
+
+bool
+FlexiNoteLayer::getPointToDrag(View *v, int x, int y, FlexiNoteModel::Point &p) const
+{
+    if (!m_model) return false;
+
+    long frame = v->getFrameForX(x);
+
+    FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
+    if (onPoints.empty()) return false;
+
+//    cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << endl;
+
+    int nearestDistance = -1;
+
+    for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin();
+         i != onPoints.end(); ++i) {
+        
+        int distance = getYForValue(v, (*i).value) - y;
+        if (distance < 0) distance = -distance;
+        if (nearestDistance == -1 || distance < nearestDistance) {
+            nearestDistance = distance;
+            p = *i;
+        }
+    }
+
+    return true;
+}
+
+bool
+FlexiNoteLayer::getNoteToEdit(View *v, int x, int y, FlexiNoteModel::Point &p) const
+{
+    // GF: find the note that is closest to the cursor
+    if (!m_model) return false;
+
+    long frame = v->getFrameForX(x);
+
+    FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
+    if (onPoints.empty()) return false;
+
+//    std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl;
+
+    int nearestDistance = -1;
+
+    for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin();
+         i != onPoints.end(); ++i) {
+        
+        int distance = getYForValue(v, (*i).value) - y;
+        if (distance < 0) distance = -distance;
+        if (nearestDistance == -1 || distance < nearestDistance) {
+            nearestDistance = distance;
+            p = *i;
+        }
+    }
+
+    return true;
+}
+
+QString
+FlexiNoteLayer::getFeatureDescription(View *v, QPoint &pos) const
+{
+    int x = pos.x();
+
+    if (!m_model || !m_model->getSampleRate()) return "";
+
+    FlexiNoteModel::PointList points = getLocalPoints(v, x);
+
+    if (points.empty()) {
+        if (!m_model->isReady()) {
+            return tr("In progress");
+        } else {
+            return tr("No local points");
+        }
+    }
+
+    FlexiNote note(0);
+    FlexiNoteModel::PointList::iterator i;
+
+    for (i = points.begin(); i != points.end(); ++i) {
+
+        int y = getYForValue(v, i->value);
+        int h = NOTE_HEIGHT; // GF: larger notes
+
+        if (m_model->getValueQuantization() != 0.0) {
+            h = y - getYForValue(v, i->value + m_model->getValueQuantization());
+            if (h < NOTE_HEIGHT) h = NOTE_HEIGHT;
+        }
+
+        // GF: this is not quite correct
+        if (pos.y() >= y - 4 && pos.y() <= y + h) {
+            note = *i;
+            break;
+        }
+    }
+
+    if (i == points.end()) return tr("No local points");
+
+    RealTime rt = RealTime::frame2RealTime(note.frame,
+                                           m_model->getSampleRate());
+    RealTime rd = RealTime::frame2RealTime(note.duration,
+                                           m_model->getSampleRate());
+    
+    QString pitchText;
+
+    if (shouldConvertMIDIToHz()) {
+
+        int mnote = lrintf(note.value);
+        int cents = lrintf((note.value - mnote) * 100);
+        float freq = Pitch::getFrequencyForPitch(mnote, cents);
+        pitchText = tr("%1 (%2, %3 Hz)")
+            .arg(Pitch::getPitchLabel(mnote, cents))
+            .arg(mnote)
+            .arg(freq);
+
+    } else if (getScaleUnits() == "Hz") {
+
+        pitchText = tr("%1 Hz (%2, %3)")
+            .arg(note.value)
+            .arg(Pitch::getPitchLabelForFrequency(note.value))
+            .arg(Pitch::getPitchForFrequency(note.value));
+
+    } else {
+        pitchText = tr("%1 %2")
+            .arg(note.value).arg(getScaleUnits());
+    }
+
+    QString text;
+
+    if (note.label == "") {
+        text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
+            .arg(rt.toText(true).c_str())
+            .arg(pitchText)
+            .arg(rd.toText(true).c_str());
+    } else {
+        text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
+            .arg(rt.toText(true).c_str())
+            .arg(pitchText)
+            .arg(rd.toText(true).c_str())
+            .arg(note.label);
+    }
+
+    pos = QPoint(v->getXForFrame(note.frame),
+                 getYForValue(v, note.value));
+    return text;
+}
+
+bool
+FlexiNoteLayer::snapToFeatureFrame(View *v, int &frame,
+                                   size_t &resolution,
+                                   SnapType snap) const
+{
+    if (!m_model) {
+        return Layer::snapToFeatureFrame(v, frame, resolution, snap);
+    }
+
+    resolution = m_model->getResolution();
+    FlexiNoteModel::PointList points;
+
+    if (snap == SnapNeighbouring) {
+    
+        points = getLocalPoints(v, v->getXForFrame(frame));
+        if (points.empty()) return false;
+        frame = points.begin()->frame;
+        return true;
+    }    
+
+    points = m_model->getPoints(frame, frame);
+    int snapped = frame;
+    bool found = false;
+
+    for (FlexiNoteModel::PointList::const_iterator i = points.begin();
+         i != points.end(); ++i) {
+
+        cerr << "FlexiNoteModel: point at " << i->frame << endl;
+
+        if (snap == SnapRight) {
+
+            if (i->frame > frame) {
+                snapped = i->frame;
+                found = true;
+                break;
+            } else if (i->frame + i->duration >= frame) {
+                snapped = i->frame + i->duration;
+                found = true;
+                break;
+            }
+
+        } else if (snap == SnapLeft) {
+
+            if (i->frame <= frame) {
+                snapped = i->frame;
+                found = true; // don't break, as the next may be better
+            } else {
+                break;
+            }
+
+        } else { // nearest
+
+            FlexiNoteModel::PointList::const_iterator j = i;
+            ++j;
+
+            if (j == points.end()) {
+
+                snapped = i->frame;
+                found = true;
+                break;
+
+            } else if (j->frame >= frame) {
+
+                if (j->frame - frame < frame - i->frame) {
+                    snapped = j->frame;
+                } else {
+                    snapped = i->frame;
+                }
+                found = true;
+                break;
+            }
+        }
+    }
+
+    cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl;
+
+    frame = snapped;
+    return found;
+}
+
+void
+FlexiNoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
+{
+    min = 0.0;
+    max = 0.0;
+    log = false;
+
+    QString queryUnits;
+    if (shouldConvertMIDIToHz()) queryUnits = "Hz";
+    else queryUnits = getScaleUnits();
+
+    if (shouldAutoAlign()) {
+
+        if (!v->getValueExtents(queryUnits, min, max, log)) {
+
+            min = m_model->getValueMinimum();
+            max = m_model->getValueMaximum();
+
+            if (shouldConvertMIDIToHz()) {
+                min = Pitch::getFrequencyForPitch(lrintf(min));
+                max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+            }
+
+#ifdef DEBUG_NOTE_LAYER
+            cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
+#endif
+
+        } else if (log) {
+
+            LogRange::mapRange(min, max);
+
+#ifdef DEBUG_NOTE_LAYER
+            cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
+#endif
+        }
+
+    } else {
+
+        getDisplayExtents(min, max);
+
+        if (m_verticalScale == MIDIRangeScale) {
+            min = Pitch::getFrequencyForPitch(0);
+            max = Pitch::getFrequencyForPitch(70);
+        } else if (shouldConvertMIDIToHz()) {
+            min = Pitch::getFrequencyForPitch(lrintf(min));
+            max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+        }
+
+        if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
+            LogRange::mapRange(min, max);
+            log = true;
+        }
+    }
+
+    if (max == min) max = min + 1.0;
+}
+
+int
+FlexiNoteLayer::getYForValue(View *v, float val) const
+{
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
+    int h = v->height();
+
+    getScaleExtents(v, min, max, logarithmic);
+
+#ifdef DEBUG_NOTE_LAYER
+    cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
+#endif
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getFrequencyForPitch(lrintf(val),
+                                          lrintf((val - lrintf(val)) * 100));
+#ifdef DEBUG_NOTE_LAYER
+        cerr << "shouldConvertMIDIToHz true, val now = " << val << endl;
+#endif
+    }
+
+    if (logarithmic) {
+        val = LogRange::map(val);
+#ifdef DEBUG_NOTE_LAYER
+        cerr << "logarithmic true, val now = " << val << endl;
+#endif
+    }
+
+    int y = int(h - ((val - min) * h) / (max - min)) - 1;
+#ifdef DEBUG_NOTE_LAYER
+    cerr << "y = " << y << endl;
+#endif
+    return y;
+}
+
+float
+FlexiNoteLayer::getValueForY(View *v, int y) const
+{
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
+    int h = v->height();
+
+    getScaleExtents(v, min, max, logarithmic);
+
+    float val = min + (float(h - y) * float(max - min)) / h;
+
+    if (logarithmic) {
+        val = powf(10.f, val);
+    }
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getPitchForFrequency(val);
+    }
+
+    return val;
+}
+
+bool
+FlexiNoteLayer::shouldAutoAlign() const
+{
+    if (!m_model) return false;
+    return (m_verticalScale == AutoAlignScale);
+}
+
+void
+FlexiNoteLayer::paint(View *v, QPainter &paint, QRect rect) const
+{
+    if (!m_model || !m_model->isOK()) return;
+
+    int sampleRate = m_model->getSampleRate();
+    if (!sampleRate) return;
+
+//    Profiler profiler("FlexiNoteLayer::paint", true);
+
+    int x0 = rect.left(), x1 = rect.right();
+    long frame0 = v->getFrameForX(x0);
+    long frame1 = v->getFrameForX(x1);
+
+    FlexiNoteModel::PointList points(m_model->getPoints(frame0, frame1));
+    if (points.empty()) return;
+
+    paint.setPen(getBaseQColor());
+
+    QColor brushColour(getBaseQColor());
+    brushColour.setAlpha(80);
+
+//    SVDEBUG << "FlexiNoteLayer::paint: resolution is "
+//        << m_model->getResolution() << " frames" << endl;
+
+    float min = m_model->getValueMinimum();
+    float max = m_model->getValueMaximum();
+    if (max == min) max = min + 1.0;
+
+    QPoint localPos;
+    FlexiNoteModel::Point illuminatePoint(0);
+    bool shouldIlluminate = false;
+
+    if (v->shouldIlluminateLocalFeatures(this, localPos)) {
+        shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
+                                          illuminatePoint);
+    }
+
+    paint.save();
+    paint.setRenderHint(QPainter::Antialiasing, false);
+    
+    for (FlexiNoteModel::PointList::const_iterator i = points.begin();
+         i != points.end(); ++i) {
+
+        const FlexiNoteModel::Point &p(*i);
+
+        int x = v->getXForFrame(p.frame);
+        int y = getYForValue(v, p.value);
+        int w = v->getXForFrame(p.frame + p.duration) - x;
+        int h = NOTE_HEIGHT; //GF: larger notes
+    
+        if (m_model->getValueQuantization() != 0.0) {
+            h = y - getYForValue(v, p.value + m_model->getValueQuantization());
+            if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes
+        }
+
+        if (w < 1) w = 1;
+        paint.setPen(getBaseQColor());
+        paint.setBrush(brushColour);
+
+        // if (shouldIlluminate &&
+        //         // "illuminatePoint == p"
+        //         !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) &&
+        //         !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) {
+        // 
+        //         paint.setPen(v->getForeground());
+        //         paint.setBrush(v->getForeground());
+        // 
+        //         QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+        //         v->drawVisibleText(paint, 
+        //                            x - paint.fontMetrics().width(vlabel) - 2,
+        //                            y + paint.fontMetrics().height()/2
+        //                              - paint.fontMetrics().descent(), 
+        //                            vlabel, View::OutlinedText);
+        // 
+        //         QString hlabel = RealTime::frame2RealTime
+        //             (p.frame, m_model->getSampleRate()).toText(true).c_str();
+        //         v->drawVisibleText(paint, 
+        //                            x,
+        //                            y - h/2 - paint.fontMetrics().descent() - 2,
+        //                            hlabel, View::OutlinedText);
+        // }
+    
+        paint.drawRect(x, y - h/2, w, h);
+    }
+
+    paint.restore();
+}
+
+int
+FlexiNoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
+{
+    if (!m_model || shouldAutoAlign()) {
+        return 0;
+    } else  {
+        if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
+            return LogNumericalScale().getWidth(v, paint) + 10; // for piano
+        } else {
+            return LinearNumericalScale().getWidth(v, paint);
+        }
+    }
+}
+
+void
+FlexiNoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+{
+    if (!m_model || m_model->getPoints().empty()) return;
+
+    QString unit;
+    float min, max;
+    bool logarithmic;
+
+    int w = getVerticalScaleWidth(v, false, paint);
+    int h = v->height();
+
+    getScaleExtents(v, min, max, logarithmic);
+
+    if (logarithmic) {
+        LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
+    } else {
+        LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
+    }
+    
+    if (logarithmic && (getScaleUnits() == "Hz")) {
+        PianoScale().paintPianoVertical
+            (v, paint, QRect(w - 10, 0, 10, h), 
+             LogRange::unmap(min), 
+             LogRange::unmap(max));
+        paint.drawLine(w, 0, w, h);
+    }
+        
+    if (getScaleUnits() != "") {
+        int mw = w - 5;
+        paint.drawText(5,
+                       5 + paint.fontMetrics().ascent(),
+                       TextAbbrev::abbreviate(getScaleUnits(),
+                                              paint.fontMetrics(),
+                                              mw));
+    }
+}
+
+void
+FlexiNoteLayer::drawStart(View *v, QMouseEvent *e)
+{
+//    SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
+
+    if (!m_model) return;
+
+    long frame = v->getFrameForX(e->x());
+    if (frame < 0) frame = 0;
+    frame = frame / m_model->getResolution() * m_model->getResolution();
+
+    float value = getValueForY(v, e->y());
+
+    m_editingPoint = FlexiNoteModel::Point(frame, value, 0, 0.8, tr("New Point"));
+    m_originalPoint = m_editingPoint;
+
+    if (m_editingCommand) finish(m_editingCommand);
+    m_editingCommand = new FlexiNoteModel::EditCommand(m_model,
+                                                       tr("Draw Point"));
+    m_editingCommand->addPoint(m_editingPoint);
+
+    m_editing = true;
+}
+
+void
+FlexiNoteLayer::drawDrag(View *v, QMouseEvent *e)
+{
+//    SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
+
+    if (!m_model || !m_editing) return;
+
+    long frame = v->getFrameForX(e->x());
+    if (frame < 0) frame = 0;
+    frame = frame / m_model->getResolution() * m_model->getResolution();
+
+    float newValue = getValueForY(v, e->y());
+
+    long newFrame = m_editingPoint.frame;
+    long newDuration = frame - newFrame;
+    if (newDuration < 0) {
+        newFrame = frame;
+        newDuration = -newDuration;
+    } else if (newDuration == 0) {
+        newDuration = 1;
+    }
+
+    m_editingCommand->deletePoint(m_editingPoint);
+    m_editingPoint.frame = newFrame;
+    m_editingPoint.value = newValue;
+    m_editingPoint.duration = newDuration;
+    m_editingCommand->addPoint(m_editingPoint);
+}
+
+void
+FlexiNoteLayer::drawEnd(View *, QMouseEvent *)
+{
+//    SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
+    if (!m_model || !m_editing) return;
+    finish(m_editingCommand);
+    m_editingCommand = 0;
+    m_editing = false;
+}
+
+void
+FlexiNoteLayer::eraseStart(View *v, QMouseEvent *e)
+{
+    if (!m_model) return;
+
+    if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
+
+    if (m_editingCommand) {
+        finish(m_editingCommand);
+        m_editingCommand = 0;
+    }
+
+    m_editing = true;
+}
+
+void
+FlexiNoteLayer::eraseDrag(View *v, QMouseEvent *e)
+{
+}
+
+void
+FlexiNoteLayer::eraseEnd(View *v, QMouseEvent *e)
+{
+    if (!m_model || !m_editing) return;
+
+    m_editing = false;
+
+    FlexiNoteModel::Point p(0);
+    if (!getPointToDrag(v, e->x(), e->y(), p)) return;
+    if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
+
+    m_editingCommand = new FlexiNoteModel::EditCommand(m_model, tr("Erase Point"));
+
+    m_editingCommand->deletePoint(m_editingPoint);
+
+    finish(m_editingCommand);
+    m_editingCommand = 0;
+    m_editing = false;
+}
+
+void
+FlexiNoteLayer::editStart(View *v, QMouseEvent *e)
+{
+//    SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
+    std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
+
+    if (!m_model) return;
+
+    if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
+    m_originalPoint = FlexiNote(m_editingPoint);
+    
+    if (m_editMode == RightBoundary) {
+        m_dragPointX =   v->getXForFrame(m_editingPoint.frame + m_editingPoint.duration);
+    } else {
+        m_dragPointX = v->getXForFrame(m_editingPoint.frame);
+    }
+    m_dragPointY = getYForValue(v, m_editingPoint.value);
+
+    if (m_editingCommand) {
+        finish(m_editingCommand);
+        m_editingCommand = 0;
+    }
+
+    m_editing = true;
+    m_dragStartX = e->x();
+    m_dragStartY = e->y();
+    
+    long onset = m_originalPoint.frame;
+    long offset = m_originalPoint.frame + m_originalPoint.duration - 1;
+    
+    m_greatestLeftNeighbourFrame = -1;
+    m_smallestRightNeighbourFrame = std::numeric_limits<long>::max();
+    
+    for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
+         i != m_model->getPoints().end(); ++i) {
+        FlexiNote currentNote = *i;
+        
+        // left boundary
+        if (currentNote.frame + currentNote.duration - 1 < onset) {
+            m_greatestLeftNeighbourFrame = currentNote.frame + currentNote.duration - 1;
+        }
+        
+        // right boundary
+        if (currentNote.frame > offset) {
+            m_smallestRightNeighbourFrame = currentNote.frame;
+            break;
+        }
+    }
+    std::cerr << "note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl;
+}
+
+void
+FlexiNoteLayer::editDrag(View *v, QMouseEvent *e)
+{
+//    SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
+    std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+
+    if (!m_model || !m_editing) return;
+
+    int xdist = e->x() - m_dragStartX;
+    int ydist = e->y() - m_dragStartY;
+    int newx = m_dragPointX + xdist;
+    int newy = m_dragPointY + ydist;
+
+    long dragFrame = v->getFrameForX(newx);
+    if (dragFrame < 0) dragFrame = 0;
+    dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution();
+    
+    float value = getValueForY(v, newy);
+
+    if (!m_editingCommand) {
+        m_editingCommand = new FlexiNoteModel::EditCommand(m_model,
+                                                           tr("Drag Point"));
+    }
+
+    m_editingCommand->deletePoint(m_editingPoint);
+
+    std::cerr << "edit mode: " << m_editMode << std::endl;
+    
+    switch (m_editMode) {
+    case LeftBoundary : {
+        // left 
+        if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
+        // right
+        if (m_intelligentActions && dragFrame >= m_originalPoint.frame + m_originalPoint.duration) {
+            dragFrame = m_originalPoint.frame + m_originalPoint.duration - 1;
+        }
+        m_editingPoint.frame = dragFrame;
+        m_editingPoint.duration = m_originalPoint.frame - dragFrame + m_originalPoint.duration;
+        break;
+    }
+    case RightBoundary : {
+        // left
+        if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
+        if (m_intelligentActions && dragFrame >= m_smallestRightNeighbourFrame) dragFrame = m_smallestRightNeighbourFrame - 1;
+        m_editingPoint.duration = dragFrame - m_originalPoint.frame + 1;
+        break;
+    }
+    case DragNote : {
+        // left
+        if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
+        // right
+        if (m_intelligentActions && dragFrame + m_originalPoint.duration >= m_smallestRightNeighbourFrame) {
+            dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.duration;
+        }
+        m_editingPoint.frame = dragFrame;
+        m_editingPoint.value = value;
+        break;
+    }
+    }
+    m_editingCommand->addPoint(m_editingPoint);
+    std::cerr << "added new point(" << m_editingPoint.frame << "," << m_editingPoint.duration << ")" << std::endl;
+    
+}
+
+void
+FlexiNoteLayer::editEnd(View *v, QMouseEvent *e)
+{
+//    SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
+    std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+    
+    if (!m_model || !m_editing) return;
+
+    if (m_editingCommand) {
+
+        QString newName = m_editingCommand->getName();
+
+        if (m_editingPoint.frame != m_originalPoint.frame) {
+            if (m_editingPoint.value != m_originalPoint.value) {
+                newName = tr("Edit Point");
+            } else {
+                newName = tr("Relocate Point");
+            }
+        } else {
+            newName = tr("Change Point Value");
+        }
+
+        m_editingCommand->setName(newName);
+        finish(m_editingCommand);
+    }
+
+    m_editingCommand = 0;
+    m_editing = false;
+}
+
+void
+FlexiNoteLayer::splitStart(View *v, QMouseEvent *e)
+{
+    // GF: note splitting starts (!! remove printing soon)
+    std::cerr << "splitStart" << std::endl;
+    if (!m_model) return;
+
+    if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
+    // m_originalPoint = m_editingPoint;
+    // 
+    // m_dragPointX = v->getXForFrame(m_editingPoint.frame);
+    // m_dragPointY = getYForValue(v, m_editingPoint.value);
+
+    if (m_editingCommand) {
+        finish(m_editingCommand);
+        m_editingCommand = 0;
+    }
+
+    m_editing = true;
+    m_dragStartX = e->x();
+    m_dragStartY = e->y();
+    
+}
+
+void
+FlexiNoteLayer::splitEnd(View *v, QMouseEvent *e)
+{
+    // GF: note splitting ends. (!! remove printing soon)
+    std::cerr << "splitEnd" << std::endl;
+    if (!m_model || !m_editing || m_editMode != SplitNote) return;
+
+    int xdist = e->x() - m_dragStartX;
+    int ydist = e->y() - m_dragStartY;
+    if (xdist != 0 || ydist != 0) { 
+        std::cerr << "mouse moved" << std::endl;    
+        return; 
+    }
+
+    // MM: simpler declaration 
+    FlexiNote note(0);
+    if (!getPointToDrag(v, e->x(), e->y(), note)) return;
+
+    long frame = v->getFrameForX(e->x());
+
+    int gap = 0; // MM: I prefer a gap of 0, but we can decide later
+    
+    // MM: changed this a bit, to make it slightly clearer (// GF: nice changes!)
+    FlexiNote newNote1(note.frame, note.value, 
+                       frame - note.frame - gap, 
+                       note.level, note.label);
+    
+    FlexiNote newNote2(frame, note.value, 
+                       note.duration - newNote1.duration, 
+                       note.level, note.label);
+                       
+    if (m_intelligentActions) {
+        updateNoteValue(v,newNote1);
+        updateNoteValue(v,newNote2);
+    }
+
+    FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
+        (m_model, tr("Edit Point"));
+    command->deletePoint(note);
+    if ((e->modifiers() & Qt::ShiftModifier)) {
+        finish(command);
+        return;
+    }
+    command->addPoint(newNote1);
+    command->addPoint(newNote2);
+    finish(command);
+    
+}
+
+void
+FlexiNoteLayer::addNote(View *v, QMouseEvent *e)
+{
+    std::cerr << "addNote" << std::endl;
+    if (!m_model) return;
+
+    long duration = 10000;
+    
+    long frame = v->getFrameForX(e->x());
+    float value = getValueForY(v, e->y());
+    
+    if (m_intelligentActions) {
+        long smallestRightNeighbourFrame = 0;
+        for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
+             i != m_model->getPoints().end(); ++i) {
+            FlexiNote currentNote = *i;
+            if (currentNote.frame > frame) {
+                smallestRightNeighbourFrame = currentNote.frame;
+                break;
+            }
+        }
+        
+        duration = std::min(smallestRightNeighbourFrame - frame + 1, duration);
+        duration = (duration > 0) ? duration : 0;
+    }
+
+    if (!m_intelligentActions || 
+        m_model->getPoints(frame).empty() && duration > 0)
+    {
+        FlexiNote newNote(frame, value, duration, 100, "new note");
+        FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
+            (m_model, tr("Add Point"));
+        command->addPoint(newNote);
+        finish(command);
+    }
+}
+
+
+void
+FlexiNoteLayer::updateNoteValue(View *v, FlexiNoteModel::Point &note) const
+{
+    //GF: update the note value conforming the median of pitch values in the underlying note layer
+    Layer *layer = v->getLayer(1); // GF: !!! gross assumption about correct layer order
+    SparseTimeValueModel *model = 0;
+    if (layer && layer->getModel()) 
+        model = dynamic_cast<SparseTimeValueModel *>(layer->getModel());
+        
+    if (!model) return;
+        
+    std::cerr << model->getTypeName() << std::endl;
+
+    SparseModel<TimeValuePoint>::PointList dataPoints = model->getPoints(note.frame, note.frame + note.duration);
+    if (dataPoints.empty()) return;
+   
+    // std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl;
+   
+    std::vector<float> pitchValues;
+   
+    for (SparseModel<TimeValuePoint>::PointList::const_iterator i = dataPoints.begin(); 
+         i != dataPoints.end(); ++i) {
+        pitchValues.push_back((*i).value);
+    }
+    sort(pitchValues.begin(), pitchValues.end());
+    size_t size = pitchValues.size();
+    double median;
+
+    if (size % 2 == 0) {
+        median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2;
+    } else {
+        median = pitchValues[size/2];
+    }
+    
+    note.value = median;
+}
+
+void 
+FlexiNoteLayer::mouseMoveEvent(View *v, QMouseEvent *e)
+{
+    // GF: context sensitive cursors
+    // v->setCursor(Qt::ArrowCursor);
+    FlexiNoteModel::Point note(0);
+    if (!getNoteToEdit(v, e->x(), e->y(), note)) { 
+        // v->setCursor(Qt::UpArrowCursor);
+        return; 
+    }
+
+    bool closeToLeft = false, closeToRight = false, closeToTop = false, closeToBottom = false;
+    getRelativeMousePosition(v, note, e->x(), e->y(), closeToLeft, closeToRight, closeToTop, closeToBottom);
+    // if (!closeToLeft) return;
+    // if (closeToTop) v->setCursor(Qt::SizeVerCursor);
+    
+    if (closeToLeft) { v->setCursor(Qt::SizeHorCursor); m_editMode = LeftBoundary; return; }
+    if (closeToRight) { v->setCursor(Qt::SizeHorCursor); m_editMode = RightBoundary; return; }
+    if (closeToTop) { v->setCursor(Qt::CrossCursor);  m_editMode = DragNote; return; }
+    if (closeToBottom) { v->setCursor(Qt::UpArrowCursor); m_editMode = SplitNote; return; }
+
+    v->setCursor(Qt::ArrowCursor);
+
+    // std::cerr << "Mouse moved in edit mode over FlexiNoteLayer" << std::endl;
+    // v->setCursor(Qt::SizeHorCursor);
+
+}
+
+void
+FlexiNoteLayer::getRelativeMousePosition(View *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;
+
+    int ctol = 0;
+    int noteStartX = v->getXForFrame(note.frame);
+    int noteEndX = v->getXForFrame(note.frame + note.duration);
+    int noteValueY = getYForValue(v,note.value);
+    int noteStartY = noteValueY - (NOTE_HEIGHT / 2);
+    int noteEndY = noteValueY + (NOTE_HEIGHT / 2);
+    
+    bool closeToNote = false;
+    
+    if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true;
+    if (!closeToNote) return;
+    
+    int tol = NOTE_HEIGHT / 2;
+    
+    if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true;
+    if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true;
+    if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true;
+    if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true;
+
+//    cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl;
+}
+
+
+bool
+FlexiNoteLayer::editOpen(View *v, QMouseEvent *e)
+{
+    std::cerr << "Opening note editor dialog" << std::endl;
+    if (!m_model) return false;
+
+    FlexiNoteModel::Point note(0);
+    if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
+
+//    FlexiNoteModel::Point note = *points.begin();
+
+    ItemEditDialog *dialog = new ItemEditDialog
+        (m_model->getSampleRate(),
+         ItemEditDialog::ShowTime |
+         ItemEditDialog::ShowDuration |
+         ItemEditDialog::ShowValue |
+         ItemEditDialog::ShowText,
+         getScaleUnits());
+
+    dialog->setFrameTime(note.frame);
+    dialog->setValue(note.value);
+    dialog->setFrameDuration(note.duration);
+    dialog->setText(note.label);
+
+    if (dialog->exec() == QDialog::Accepted) {
+
+        FlexiNoteModel::Point newNote = note;
+        newNote.frame = dialog->getFrameTime();
+        newNote.value = dialog->getValue();
+        newNote.duration = dialog->getFrameDuration();
+        newNote.label = dialog->getText();
+        
+        FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
+            (m_model, tr("Edit Point"));
+        command->deletePoint(note);
+        command->addPoint(newNote);
+        finish(command);
+    }
+
+    delete dialog;
+    return true;
+}
+
+void
+FlexiNoteLayer::moveSelection(Selection s, size_t newStartFrame)
+{
+    if (!m_model) return;
+
+    FlexiNoteModel::EditCommand *command =
+        new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection"));
+
+    FlexiNoteModel::PointList points =
+        m_model->getPoints(s.getStartFrame(), s.getEndFrame());
+
+    for (FlexiNoteModel::PointList::iterator i = points.begin();
+         i != points.end(); ++i) {
+
+        if (s.contains(i->frame)) {
+            FlexiNoteModel::Point newPoint(*i);
+            newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
+            command->deletePoint(*i);
+            command->addPoint(newPoint);
+        }
+    }
+
+    finish(command);
+}
+
+void
+FlexiNoteLayer::resizeSelection(Selection s, Selection newSize)
+{
+    if (!m_model) return;
+
+    FlexiNoteModel::EditCommand *command =
+        new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection"));
+
+    FlexiNoteModel::PointList points =
+        m_model->getPoints(s.getStartFrame(), s.getEndFrame());
+
+    double ratio =
+        double(newSize.getEndFrame() - newSize.getStartFrame()) /
+        double(s.getEndFrame() - s.getStartFrame());
+
+    for (FlexiNoteModel::PointList::iterator i = points.begin();
+         i != points.end(); ++i) {
+
+        if (s.contains(i->frame)) {
+
+            double targetStart = i->frame;
+            targetStart = newSize.getStartFrame() + 
+                double(targetStart - s.getStartFrame()) * ratio;
+
+            double targetEnd = i->frame + i->duration;
+            targetEnd = newSize.getStartFrame() +
+                double(targetEnd - s.getStartFrame()) * ratio;
+
+            FlexiNoteModel::Point newPoint(*i);
+            newPoint.frame = lrint(targetStart);
+            newPoint.duration = lrint(targetEnd - targetStart);
+            command->deletePoint(*i);
+            command->addPoint(newPoint);
+        }
+    }
+
+    finish(command);
+}
+
+void
+FlexiNoteLayer::deleteSelection(Selection s)
+{
+    if (!m_model) return;
+
+    FlexiNoteModel::EditCommand *command =
+        new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points"));
+
+    FlexiNoteModel::PointList points =
+        m_model->getPoints(s.getStartFrame(), s.getEndFrame());
+
+    for (FlexiNoteModel::PointList::iterator i = points.begin();
+         i != points.end(); ++i) {
+
+        if (s.contains(i->frame)) {
+            command->deletePoint(*i);
+        }
+    }
+
+    finish(command);
+}    
+
+void
+FlexiNoteLayer::copy(View *v, Selection s, Clipboard &to)
+{
+    if (!m_model) return;
+
+    FlexiNoteModel::PointList points =
+        m_model->getPoints(s.getStartFrame(), s.getEndFrame());
+
+    for (FlexiNoteModel::PointList::iterator i = points.begin();
+         i != points.end(); ++i) {
+        if (s.contains(i->frame)) {
+            Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
+            point.setReferenceFrame(alignToReference(v, i->frame));
+            to.addPoint(point);
+        }
+    }
+}
+
+bool
+FlexiNoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
+{
+    if (!m_model) return false;
+
+    const Clipboard::PointList &points = from.getPoints();
+
+    bool realign = false;
+
+    if (clipboardHasDifferentAlignment(v, from)) {
+
+        QMessageBox::StandardButton button =
+            QMessageBox::question(v, 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);
+
+        if (button == QMessageBox::Cancel) {
+            return false;
+        }
+
+        if (button == QMessageBox::Yes) {
+            realign = true;
+        }
+    }
+
+    FlexiNoteModel::EditCommand *command =
+        new FlexiNoteModel::EditCommand(m_model, tr("Paste"));
+
+    for (Clipboard::PointList::const_iterator i = points.begin();
+         i != points.end(); ++i) {
+        
+        if (!i->haveFrame()) continue;
+        size_t frame = 0;
+
+        if (!realign) {
+            
+            frame = i->getFrame();
+
+        } else {
+
+            if (i->haveReferenceFrame()) {
+                frame = i->getReferenceFrame();
+                frame = alignFromReference(v, frame);
+            } else {
+                frame = i->getFrame();
+            }
+        }
+
+        FlexiNoteModel::Point newPoint(frame);
+  
+        if (i->haveLabel()) newPoint.label = i->getLabel();
+        if (i->haveValue()) newPoint.value = i->getValue();
+        else newPoint.value = (m_model->getValueMinimum() +
+                               m_model->getValueMaximum()) / 2;
+        if (i->haveLevel()) newPoint.level = i->getLevel();
+        if (i->haveDuration()) newPoint.duration = i->getDuration();
+        else {
+            size_t nextFrame = frame;
+            Clipboard::PointList::const_iterator j = i;
+            for (; j != points.end(); ++j) {
+                if (!j->haveFrame()) continue;
+                if (j != i) break;
+            }
+            if (j != points.end()) {
+                nextFrame = j->getFrame();
+            }
+            if (nextFrame == frame) {
+                newPoint.duration = m_model->getResolution();
+            } else {
+                newPoint.duration = nextFrame - frame;
+            }
+        }
+        
+        command->addPoint(newPoint);
+    }
+
+    finish(command);
+    return true;
+}
+
+void
+FlexiNoteLayer::addNoteOn(long frame, int pitch, int velocity)
+{
+    m_pendingNoteOns.insert(FlexiNote(frame, pitch, 0, float(velocity) / 127.0, ""));
+}
+
+void
+FlexiNoteLayer::addNoteOff(long frame, int pitch)
+{
+    for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin();
+         i != m_pendingNoteOns.end(); ++i) {
+        if (lrintf((*i).value) == pitch) {
+            FlexiNote note(*i);
+            m_pendingNoteOns.erase(i);
+            note.duration = frame - note.frame;
+            if (m_model) {
+                FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand
+                    (m_model, note, tr("Record FlexiNote"));
+                // execute and bundle:
+                CommandHistory::getInstance()->addCommand(c, true, true);
+            }
+            break;
+        }
+    }
+}
+
+void
+FlexiNoteLayer::abandonNoteOns()
+{
+    m_pendingNoteOns.clear();
+}
+
+int
+FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "White" : "Black"));
+}
+
+void
+FlexiNoteLayer::toXml(QTextStream &stream,
+                      QString indent, QString extraAttributes) const
+{
+    SingleColourLayer::toXml(stream, indent, extraAttributes +
+                             QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
+                             .arg(m_verticalScale)
+                             .arg(m_scaleMinimum)
+                             .arg(m_scaleMaximum));
+}
+
+void
+FlexiNoteLayer::setProperties(const QXmlAttributes &attributes)
+{
+    SingleColourLayer::setProperties(attributes);
+
+    bool ok, alsoOk;
+    VerticalScale scale = (VerticalScale)
+        attributes.value("verticalScale").toInt(&ok);
+    if (ok) setVerticalScale(scale);
+
+    float min = attributes.value("scaleMinimum").toFloat(&ok);
+    float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
+//    if (ok && alsoOk && min != max) setDisplayExtents(min, max);
+}
+
+void
+FlexiNoteLayer::setVerticalRangeToNoteRange(View *v)
+{
+    float minf = std::numeric_limits<float>::max();
+    float maxf = 0;
+    bool hasNotes = 0;
+    for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
+         i != m_model->getPoints().end(); ++i) {
+        hasNotes = 1;
+        FlexiNote note = *i;
+        if (note.value < minf) minf = note.value;
+        if (note.value > maxf) maxf = note.value;
+    }
+    
+    std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl;
+    
+    if (hasNotes) {
+        v->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.
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/FlexiNoteLayer.h	Tue Jan 28 15:02:09 2014 +0000
@@ -0,0 +1,204 @@
+/* -*- 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 file copyright 2006 Chris Cannam.
+    
+    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 _FLEXINOTE_LAYER_H_
+#define _FLEXINOTE_LAYER_H_
+
+#define NOTE_HEIGHT 16
+
+#include "SingleColourLayer.h"
+#include "VerticalScaleLayer.h"
+
+#include "data/model/FlexiNoteModel.h"
+
+#include <QObject>
+#include <QColor>
+
+class View;
+class QPainter;
+
+
+class FlexiNoteLayer : public SingleColourLayer,
+                       public VerticalScaleLayer
+{
+    Q_OBJECT
+
+public:
+    FlexiNoteLayer();
+
+    virtual void paint(View *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 QString getFeatureDescription(View *v, QPoint &) const;
+
+    virtual bool snapToFeatureFrame(View *v, int &frame,
+                    size_t &resolution,
+                    SnapType snap) const;
+
+    virtual void drawStart(View *v, QMouseEvent *);
+    virtual void drawDrag(View *v, QMouseEvent *);
+    virtual void drawEnd(View *v, QMouseEvent *);
+
+    virtual void eraseStart(View *v, QMouseEvent *);
+    virtual void eraseDrag(View *v, QMouseEvent *);
+    virtual void eraseEnd(View *v, QMouseEvent *);
+
+    virtual void editStart(View *v, QMouseEvent *);
+    virtual void editDrag(View *v, QMouseEvent *);
+    virtual void editEnd(View *v, QMouseEvent *);
+
+    virtual void splitStart(View *v, QMouseEvent *);
+    virtual void splitEnd(View *v, QMouseEvent *);
+    
+    virtual void addNote(View *v, QMouseEvent *e);
+
+    virtual void mouseMoveEvent(View *v, QMouseEvent *);
+
+    virtual bool editOpen(View *v, QMouseEvent *);
+
+    virtual void moveSelection(Selection s, size_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, int frameOffset,
+                       bool interactive);
+
+    virtual const Model *getModel() const { return m_model; }
+    void setModel(FlexiNoteModel *model);
+
+    virtual PropertyList getProperties() const;
+    virtual QString getPropertyLabel(const PropertyName &) const;
+    virtual PropertyType getPropertyType(const PropertyName &) const;
+    virtual QString getPropertyGroupName(const PropertyName &) const;
+    virtual int getPropertyRangeAndValue(const PropertyName &,
+                                         int *min, int *max, int *deflt) const;
+    virtual QString getPropertyValueLabel(const PropertyName &,
+                      int value) const;
+    virtual void setProperty(const PropertyName &, int value);
+
+    enum VerticalScale {
+        AutoAlignScale,
+        LinearScale,
+        LogScale,
+        MIDIRangeScale
+    };
+    
+    //GF: Tonioni: context sensitive note edit actions (denoted clockwise from top).
+    enum EditMode {
+        DragNote,
+        RightBoundary,
+        SplitNote,
+        LeftBoundary
+    };
+    
+    void setIntelligentActions(bool on) { m_intelligentActions=on; }
+
+    void setVerticalScale(VerticalScale scale);
+    VerticalScale getVerticalScale() const { return m_verticalScale; }
+
+    virtual bool isLayerScrollable(const View *v) const;
+
+    virtual bool isLayerEditable() const { return true; }
+
+    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &log, QString &unit) const;
+
+    virtual bool getDisplayExtents(float &min, float &max) const;
+    virtual bool setDisplayExtents(float min, float max);
+
+    virtual int getVerticalZoomSteps(int &defaultStep) const;
+    virtual int getCurrentVerticalZoomStep() const;
+    virtual void setVerticalZoomStep(int);
+    virtual RangeMapper *getNewVerticalZoomRangeMapper() const;
+
+    /**
+     * Add a note-on.  Used when recording MIDI "live".  The note will
+     * not be finally added to the layer until the corresponding
+     * note-off.
+     */
+    void addNoteOn(long frame, int pitch, int velocity);
+    
+    /**
+     * Add a note-off.  This will cause a note to appear, if and only
+     * if there is a matching pending note-on.
+     */
+    void addNoteOff(long frame, int pitch);
+
+    /**
+     * Abandon all pending note-on events.
+     */
+    void abandonNoteOns();
+
+    virtual void toXml(QTextStream &stream, QString indent = "",
+                       QString extraAttributes = "") const;
+
+    void setProperties(const QXmlAttributes &attributes);
+    
+    void setVerticalRangeToNoteRange(View *v);
+
+    /// VerticalScaleLayer methods
+    virtual int getYForValue(View *v, float value) const;
+    virtual float getValueForY(View *v, int y) const;
+    virtual QString getScaleUnits() const;
+
+protected:
+    void getScaleExtents(View *, float &min, float &max, bool &log) const;
+    bool shouldConvertMIDIToHz() const;
+
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
+    FlexiNoteModel::PointList getLocalPoints(View *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;
+    void updateNoteValue(View *v, FlexiNoteModel::Point &note) const;
+
+    FlexiNoteModel *m_model;
+    bool m_editing;
+    bool m_intelligentActions;
+    int m_dragPointX;
+    int m_dragPointY;
+    int m_dragStartX;
+    int m_dragStartY;
+    FlexiNoteModel::Point m_originalPoint;
+    FlexiNoteModel::Point m_editingPoint;
+    long m_greatestLeftNeighbourFrame;
+    long m_smallestRightNeighbourFrame;
+    FlexiNoteModel::EditCommand *m_editingCommand;
+    VerticalScale m_verticalScale;
+    EditMode m_editMode;
+
+    typedef std::set<FlexiNoteModel::Point, FlexiNoteModel::Point::Comparator> FlexiNoteSet;
+    FlexiNoteSet m_pendingNoteOns;
+
+    mutable float m_scaleMinimum;
+    mutable float m_scaleMaximum;
+
+    bool shouldAutoAlign() const;
+
+    void finish(FlexiNoteModel::EditCommand *command) {
+        Command *c = command->finish();
+        if (c) CommandHistory::getInstance()->addCommand(c, false);
+    }
+};
+
+#endif
+
--- a/layer/Layer.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/Layer.h	Tue Jan 28 15:02:09 2014 +0000
@@ -229,6 +229,10 @@
     virtual void editDrag(View *, QMouseEvent *) { }
     virtual void editEnd(View *, QMouseEvent *) { }
 
+    virtual void splitStart(View *, QMouseEvent *) { }
+    virtual void splitEnd(View *, QMouseEvent *) { }
+    virtual void addNote(View *v, QMouseEvent *e) { };
+
     // Measurement rectangle (or equivalent).  Unlike draw and edit,
     // the base Layer class can provide working implementations of
     // these for most situations.
--- a/layer/LayerFactory.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/LayerFactory.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -21,6 +21,7 @@
 #include "TimeInstantLayer.h"
 #include "TimeValueLayer.h"
 #include "NoteLayer.h"
+#include "FlexiNoteLayer.h"
 #include "RegionLayer.h"
 #include "TextLayer.h"
 #include "ImageLayer.h"
@@ -36,6 +37,7 @@
 #include "data/model/SparseOneDimensionalModel.h"
 #include "data/model/SparseTimeValueModel.h"
 #include "data/model/NoteModel.h"
+#include "data/model/FlexiNoteModel.h"
 #include "data/model/RegionModel.h"
 #include "data/model/TextModel.h"
 #include "data/model/ImageModel.h"
@@ -73,6 +75,7 @@
     case TimeInstants: return Layer::tr("Time Instants");
     case TimeValues:   return Layer::tr("Time Values");
     case Notes:        return Layer::tr("Notes");
+    case FlexiNotes:   return Layer::tr("Flexible Notes");
     case Regions:      return Layer::tr("Regions");
     case Text:         return Layer::tr("Text");
     case Image:        return Layer::tr("Images");
@@ -161,6 +164,11 @@
 	types.insert(Notes);
     }
 
+	// NOTE: GF: types is a set, so order of insertion does not matter
+    if (dynamic_cast<FlexiNoteModel *>(model)) {
+	types.insert(FlexiNotes);
+    }
+
     if (dynamic_cast<RegionModel *>(model)) {
 	types.insert(Regions);
     }
@@ -189,6 +197,7 @@
     LayerTypeSet types;
     types.insert(TimeInstants);
     types.insert(TimeValues);
+	types.insert(FlexiNotes);
     types.insert(Notes);
     types.insert(Regions);
     types.insert(Text);
@@ -205,6 +214,7 @@
     if (dynamic_cast<const TimeRulerLayer *>(layer)) return TimeRuler;
     if (dynamic_cast<const TimeInstantLayer *>(layer)) return TimeInstants;
     if (dynamic_cast<const TimeValueLayer *>(layer)) return TimeValues;
+	if (dynamic_cast<const FlexiNoteLayer *>(layer)) return FlexiNotes;
     if (dynamic_cast<const NoteLayer *>(layer)) return Notes;
     if (dynamic_cast<const RegionLayer *>(layer)) return Regions;
     if (dynamic_cast<const TextLayer *>(layer)) return Text;
@@ -225,6 +235,7 @@
     case TimeInstants: return "instants";
     case TimeValues: return "values";
     case Notes: return "notes";
+    case FlexiNotes: return "flexible notes";
     case Regions: return "regions";
     case Text: return "text";
     case Image: return "image";
@@ -247,6 +258,7 @@
     case TimeInstants: return "timeinstants";
     case TimeValues: return "timevalues";
     case Notes: return "notes";
+    case FlexiNotes: return "flexinotes";
     case Regions: return "regions";
     case Text: return "text";
     case Image: return "image";
@@ -267,7 +279,7 @@
     if (name == "timeruler") return TimeRuler;
     if (name == "timeinstants") return TimeInstants;
     if (name == "timevalues") return TimeValues;
-    if (name == "notes") return Notes;
+    if (name == "flexinotes") return FlexiNotes;
     if (name == "regions") return Regions;
     if (name == "text") return Text;
     if (name == "image") return Image;
@@ -282,7 +294,7 @@
 {
 //    if (trySetModel<WaveformLayer, RangeSummarisableTimeValueModel>(layer, model))
 //	return;
-
+	
     if (trySetModel<WaveformLayer, WaveFileModel>(layer, model))
 	return;
 
@@ -301,9 +313,13 @@
     if (trySetModel<TimeValueLayer, SparseTimeValueModel>(layer, model))
 	return;
 
-    if (trySetModel<NoteLayer, NoteModel>(layer, model))
-	return;
+    if (trySetModel<NoteLayer, NoteModel>(layer, model)) 
+	return; 
 
+	// GF: added FlexiNoteLayer
+    if (trySetModel<FlexiNoteLayer, FlexiNoteModel>(layer, model)) 
+	return; 
+	
     if (trySetModel<RegionLayer, RegionModel>(layer, model))
 	return;
 
@@ -333,6 +349,8 @@
 	return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1);
     } else if (layerType == TimeValues) {
 	return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true);
+	} else if (layerType == FlexiNotes) {
+	return new FlexiNoteModel(baseModel->getSampleRate(), 1, true);
     } else if (layerType == Notes) {
 	return new NoteModel(baseModel->getSampleRate(), 1, true);
     } else if (layerType == Regions) {
@@ -402,6 +420,10 @@
 	layer = new TimeValueLayer;
 	break;
 
+    case FlexiNotes:
+	layer = new FlexiNoteLayer;
+	break;
+
     case Notes:
 	layer = new NoteLayer;
 	break;
--- a/layer/LayerFactory.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/LayerFactory.h	Tue Jan 28 15:02:09 2014 +0000
@@ -35,6 +35,7 @@
 	TimeInstants,
 	TimeValues,
 	Notes,
+	FlexiNotes,
 	Regions,
 	Text,
         Image,
--- a/layer/NoteLayer.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/NoteLayer.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -56,12 +56,12 @@
     m_scaleMinimum(0),
     m_scaleMaximum(0)
 {
-    
+  	SVDEBUG << "constructed NoteLayer" << endl;
 }
 
 void
 NoteLayer::setModel(NoteModel *model)
-{
+{	
     if (m_model == model) return;
     m_model = model;
 
@@ -842,7 +842,7 @@
 void
 NoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
 {
-    if (!m_model) return;
+    if (!m_model || m_model->getPoints().empty()) return;
 
     QString unit;
     float min, max;
--- a/layer/RegionLayer.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/RegionLayer.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -1069,7 +1069,7 @@
 void
 RegionLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
 {
-    if (!m_model) return;
+    if (!m_model || m_model->getPoints().empty()) return;
 
     QString unit;
     float min, max;
--- a/layer/TimeValueLayer.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/TimeValueLayer.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -1230,7 +1230,7 @@
 void
 TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
 {
-    if (!m_model) return;
+    if (!m_model || m_model->getPoints().empty()) return;
 
     QString unit;
     float min, max;
--- a/layer/WaveformLayer.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/WaveformLayer.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -43,6 +43,7 @@
     m_channelMode(SeparateChannels),
     m_channel(-1),
     m_scale(LinearScale),
+    m_middleLineHeight(0.5),
     m_aggressive(false),
     m_cache(0),
     m_cacheValid(false)
@@ -306,6 +307,15 @@
 }
 
 void
+WaveformLayer::setMiddleLineHeight(float height)
+{
+    if (m_middleLineHeight == height) return;
+    m_middleLineHeight = height;
+    m_cacheValid = false;
+    emit layerParametersChanged();
+}
+
+void
 WaveformLayer::setAggressiveCacheing(bool aggressive)
 {
     if (m_aggressive == aggressive) return;
@@ -532,6 +542,15 @@
 
     paint->setRenderHint(QPainter::Antialiasing, false);
 
+    if (m_middleLineHeight != 0.5) {
+        paint->save();
+        float space = m_middleLineHeight * 2;
+        if (space > 1.0) space = 2.0 - space;
+        float yt = h * (m_middleLineHeight - space/2);
+        paint->translate(QPointF(0, yt));
+        paint->scale(1.0, space);
+    }
+
     int x0 = 0, x1 = w - 1;
     int y0 = 0, y1 = h - 1;
 
@@ -915,6 +934,10 @@
 	}
     }
 
+    if (m_middleLineHeight != 0.5) {
+        paint->restore();
+    }
+
     if (m_aggressive) {
 
 	if (ready && rect == v->rect()) {
@@ -1314,14 +1337,16 @@
 		 "channelMode=\"%4\" "
 		 "channel=\"%5\" "
                  "scale=\"%6\" "
-		 "aggressive=\"%7\" "
-                 "autoNormalize=\"%8\"")
+                 "middleLineHeight=\"%7\" "
+		 "aggressive=\"%8\" "
+                 "autoNormalize=\"%9\"")
 	.arg(m_gain)
 	.arg(m_showMeans)
 	.arg(m_greyscale)
 	.arg(m_channelMode)
 	.arg(m_channel)
 	.arg(m_scale)
+        .arg(m_middleLineHeight)
 	.arg(m_aggressive)
         .arg(m_autoNormalize);
 
@@ -1353,10 +1378,12 @@
     int channel = attributes.value("channel").toInt(&ok);
     if (ok) setChannel(channel);
 
-    Scale scale = (Scale)
-	attributes.value("scale").toInt(&ok);
+    Scale scale = (Scale)attributes.value("scale").toInt(&ok);
     if (ok) setScale(scale);
 
+    float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
+    if (ok) setMiddleLineHeight(middleLineHeight);
+
     bool aggressive = (attributes.value("aggressive") == "1" ||
 		       attributes.value("aggressive") == "true");
     setUseGreyscale(aggressive);
--- a/layer/WaveformLayer.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/layer/WaveformLayer.h	Tue Jan 28 15:02:09 2014 +0000
@@ -147,6 +147,19 @@
     Scale getScale() const { return m_scale; }
 
     /**
+     * Specify the height of the middle of the waveform track or
+     * tracks within the layer, from 0.0 to 1.0.
+     *
+     * A value of 0.0 would indicate that the waveform occupies
+     * effectively no space at the very top of the layer; 1.0 would
+     * indicate that the waveform occupies no space at the very
+     * bottom; the default value of 0.5 indicates that it occupies the
+     * whole layer, centred at the middle.
+     */
+    void setMiddleLineHeight(float height);
+    float getMiddleLineHeight() const { return m_middleLineHeight; }
+
+    /**
      * Enable or disable aggressive pixmap cacheing.  If enabled,
      * waveforms will be rendered to an off-screen pixmap and
      * refreshed from there instead of being redrawn from the peak
@@ -216,6 +229,7 @@
     ChannelMode  m_channelMode;
     int          m_channel;
     Scale        m_scale;
+    float        m_middleLineHeight;
     bool         m_aggressive;
 
     mutable std::vector<float> m_effectiveGains;
--- a/layer/layer.pro	Wed Dec 04 14:00:27 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-TEMPLATE = lib
-
-SV_UNIT_PACKAGES = fftw3f
-load(../prf/sv.prf)
-
-CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
-QT += xml
-
-TARGET = svlayer
-
-DEPENDPATH += . ..
-INCLUDEPATH += . ..
-OBJECTS_DIR = tmp_obj
-MOC_DIR = tmp_moc
-
-# Input
-HEADERS += Colour3DPlotLayer.h \
-	   ColourDatabase.h \
-	   ColourMapper.h \
-           ImageLayer.h \
-           ImageRegionFinder.h \
-           Layer.h \
-           LayerFactory.h \
-           NoteLayer.h \
-           PaintAssistant.h \
-           RegionLayer.h \
-           SingleColourLayer.h \
-           SliceableLayer.h \
-           SliceLayer.h \
-           SpectrogramLayer.h \
-           SpectrumLayer.h \
-           TextLayer.h \
-           TimeInstantLayer.h \
-           TimeRulerLayer.h \
-           TimeValueLayer.h \
-           WaveformLayer.h
-SOURCES += Colour3DPlotLayer.cpp \
-	   ColourDatabase.cpp \
-	   ColourMapper.cpp \
-           ImageLayer.cpp \
-           ImageRegionFinder.cpp \
-           Layer.cpp \
-           LayerFactory.cpp \
-           NoteLayer.cpp \
-           PaintAssistant.cpp \
-           RegionLayer.cpp \
-           SingleColourLayer.cpp \
-           SliceLayer.cpp \
-           SpectrogramLayer.cpp \
-           SpectrumLayer.cpp \
-           TextLayer.cpp \
-           TimeInstantLayer.cpp \
-           TimeRulerLayer.cpp \
-           TimeValueLayer.cpp \
-           WaveformLayer.cpp
--- a/svgui.pro	Wed Dec 04 14:00:27 2013 +0000
+++ b/svgui.pro	Tue Jan 28 15:02:09 2014 +0000
@@ -4,10 +4,21 @@
 exists(config.pri) {
     include(config.pri)
 }
-win* {
-    !exists(config.pri) {
-        DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_RUBBERBAND HAVE_DATAQUAY HAVE_LIBLO HAVE_MAD HAVE_ID3TAG
+!exists(config.pri) {
+    win32-g++ {
+        INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include
+        LIBS += -L../sv-dependency-builds/win32-mingw/lib
     }
+    win32-msvc* {
+        INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include
+        LIBS += -L../sv-dependency-builds/win32-msvc/lib
+    }
+    macx* {
+        INCLUDEPATH += ../sv-dependency-builds/osx/include
+        LIBS += -L../sv-dependency-builds/osx/lib
+    }
+
+    DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_RUBBERBAND HAVE_LIBLO HAVE_MAD HAVE_ID3TAG
 }
 
 CONFIG += staticlib qt thread warn_on stl rtti exceptions
@@ -20,17 +31,11 @@
 OBJECTS_DIR = o
 MOC_DIR = o
 
-win32-g++ {
-    INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include
-}
-win32-msvc* {
-    INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include
-}
-
 HEADERS += layer/Colour3DPlotLayer.h \
 	   layer/ColourDatabase.h \
 	   layer/ColourMapper.h \
            layer/ColourScaleLayer.h \
+           layer/FlexiNoteLayer.h \
            layer/ImageLayer.h \
            layer/ImageRegionFinder.h \
            layer/Layer.h \
@@ -57,6 +62,7 @@
 SOURCES += layer/Colour3DPlotLayer.cpp \
 	   layer/ColourDatabase.cpp \
 	   layer/ColourMapper.cpp \
+           layer/FlexiNoteLayer.cpp \
            layer/ImageLayer.cpp \
            layer/ImageRegionFinder.cpp \
            layer/Layer.cpp \
--- a/view/Pane.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/Pane.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -25,6 +25,11 @@
 #include "base/Preferences.h"
 #include "layer/WaveformLayer.h"
 
+// GF: added so we can propagate the mouse move event to the note layer for context handling.
+#include "layer/LayerFactory.h"
+#include "layer/FlexiNoteLayer.h"
+
+
 //!!! ugh
 #include "data/model/WaveFileModel.h"
 
@@ -168,7 +173,7 @@
         }
 
         m_reset = new NotifyingPushButton;
-	m_reset->setFlat(true);
+        m_reset->setFlat(true);
         m_reset->setCursor(Qt::ArrowCursor);
         m_reset->setFixedHeight(16);
         m_reset->setFixedWidth(16);
@@ -328,19 +333,19 @@
     QPoint discard;
     bool b0, b1;
 
-    if (m_manager && m_manager->getToolMode() == ViewManager::MeasureMode) {
+    if (m_manager && m_manager->getToolModeFor(this) == ViewManager::MeasureMode) {
         return false;
     }
-
+    
     if (m_manager && !m_manager->shouldIlluminateLocalFeatures()) {
         return false;
     }
 
     if (layer == getSelectedLayer() &&
-	!shouldIlluminateLocalSelection(discard, b0, b1)) {
-
-	pos = m_identifyPoint;
-	return m_identifyFeatures;
+    !shouldIlluminateLocalSelection(discard, b0, b1)) {
+
+    pos = m_identifyPoint;
+    return m_identifyFeatures;
     }
 
     return false;
@@ -348,25 +353,25 @@
 
 bool
 Pane::shouldIlluminateLocalSelection(QPoint &pos,
-				     bool &closeToLeft,
-				     bool &closeToRight) const
+                     bool &closeToLeft,
+                     bool &closeToRight) const
 {
     if (m_identifyFeatures &&
-	m_manager &&
-	m_manager->getToolMode() == ViewManager::EditMode &&
-	!m_manager->getSelections().empty() &&
-	!selectionIsBeingEdited()) {
-
-	Selection s(getSelectionAt(m_identifyPoint.x(),
-				   closeToLeft, closeToRight));
-
-	if (!s.isEmpty()) {
-	    if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) {
-		
-		pos = m_identifyPoint;
-		return true;
-	    }
-	}
+    m_manager &&
+    m_manager->getToolModeFor(this) == ViewManager::EditMode &&
+    !m_manager->getSelections().empty() &&
+    !selectionIsBeingEdited()) {
+
+    Selection s(getSelectionAt(m_identifyPoint.x(),
+                   closeToLeft, closeToRight));
+
+    if (!s.isEmpty()) {
+        if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) {
+        
+        pos = m_identifyPoint;
+        return true;
+        }
+    }
     }
 
     return false;
@@ -376,10 +381,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;
 }
@@ -408,7 +413,7 @@
 
     if (e) paint.setClipRect(r);
 
-    ViewManager::ToolMode toolMode = m_manager->getToolMode();
+    ViewManager::ToolMode toolMode = m_manager->getToolModeFor(this);
 
     if (m_manager &&
 //        !m_manager->isPlaying() &&
@@ -626,9 +631,9 @@
         
     if (m_scaleWidth > 0 && r.left() < m_scaleWidth) {
 
-//	    Profiler profiler("Pane::paintEvent - painting vertical scale", true);
-
-//	    SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl;
+//      Profiler profiler("Pane::paintEvent - painting vertical scale", true);
+
+//      SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl;
         paint.save();
             
         paint.setPen(getForeground());
@@ -649,7 +654,7 @@
 {
     QPoint pos = m_identifyPoint;
     QString desc = topLayer->getFeatureDescription(this, pos);
-	    
+        
     if (desc != "") {
         
         paint.save();
@@ -726,7 +731,7 @@
     LayerList::iterator vi = m_layers.end();
     
     if (vi != m_layers.begin()) {
-	    
+        
         switch ((*--vi)->getPreferredFrameCountPosition()) {
             
         case Layer::PositionTop:
@@ -893,7 +898,7 @@
     }
     
     if (r.x() + r.width() >= llx - fontAscent - 3) {
-	
+    
         for (size_t i = 0; i < texts.size(); ++i) {
 
 //            cerr << "Pane "<< this << ": text " << i << ": " << texts[i] << endl;
@@ -1147,8 +1152,8 @@
 
     long testFrame = getFrameForX(x - 5);
     if (testFrame < 0) {
-	testFrame = getFrameForX(x);
-	if (testFrame < 0) return Selection();
+    testFrame = getFrameForX(x);
+    if (testFrame < 0) return Selection();
     }
 
     Selection selection = m_manager->getContainingSelection(testFrame, true);
@@ -1291,7 +1296,7 @@
     m_dragMode = UnresolvedDrag;
 
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
-    if (m_manager) mode = m_manager->getToolMode();
+    if (m_manager) mode = m_manager->getToolModeFor(this);
 
     m_navigating = false;
     m_resizing = false;
@@ -1303,12 +1308,12 @@
         (mode == ViewManager::MeasureMode &&
          (e->buttons() & Qt::LeftButton) && m_shiftPressed)) {
 
-	if (mode != ViewManager::NavigateMode) {
-	    setCursor(Qt::PointingHandCursor);
-	}
-
-	m_navigating = true;
-	m_dragCentreFrame = m_centreFrame;
+        if (mode != ViewManager::NavigateMode) {
+            setCursor(Qt::PointingHandCursor);
+        }
+
+        m_navigating = true;
+        m_dragCentreFrame = m_centreFrame;
         m_dragStartMinValue = 0;
         
         float vmin, vmax, dmin, dmax;
@@ -1320,61 +1325,70 @@
 
         if (!hasTopLayerTimeXAxis()) return;
 
-	bool closeToLeft = false, closeToRight = false;
-	Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
-
-	if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
-
-	    m_manager->removeSelection(selection);
-
-	    if (closeToLeft) {
-		m_selectionStartFrame = selection.getEndFrame();
-	    } else {
-		m_selectionStartFrame = selection.getStartFrame();
-	    }
-
-	    m_manager->setInProgressSelection(selection, false);
-	    m_resizing = true;
-	
-	} else {
-
-	    int mouseFrame = getFrameForX(e->x());
-	    size_t resolution = 1;
-	    int snapFrame = mouseFrame;
-	
-	    Layer *layer = getSelectedLayer();
-	    if (layer && !m_shiftPressed) {
-		layer->snapToFeatureFrame(this, snapFrame,
-					  resolution, Layer::SnapLeft);
-	    }
-	    
-	    if (snapFrame < 0) snapFrame = 0;
-	    m_selectionStartFrame = snapFrame;
-	    if (m_manager) {
-		m_manager->setInProgressSelection
+        bool closeToLeft = false, closeToRight = false;
+        Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
+
+        if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
+
+            m_manager->removeSelection(selection);
+
+            if (closeToLeft) {
+                m_selectionStartFrame = selection.getEndFrame();
+            } else {
+                m_selectionStartFrame = selection.getStartFrame();
+            }
+            
+            m_manager->setInProgressSelection(selection, false);
+            m_resizing = true;
+            
+        } else {
+            
+            int mouseFrame = getFrameForX(e->x());
+            size_t resolution = 1;
+            int snapFrame = mouseFrame;
+    
+            Layer *layer = getSelectedLayer();
+            if (layer && !m_shiftPressed) {
+                layer->snapToFeatureFrame(this, snapFrame,
+                                          resolution, Layer::SnapLeft);
+            }
+        
+            if (snapFrame < 0) snapFrame = 0;
+            m_selectionStartFrame = snapFrame;
+            if (m_manager) {
+                m_manager->setInProgressSelection
                     (Selection(alignToReference(snapFrame),
                                alignToReference(snapFrame + resolution)),
                      !m_ctrlPressed);
-	    }
-
-	    m_resizing = false;
-	}
-
-	update();
+            }
+
+            m_resizing = false;
+        }
+
+        update();
 
     } else if (mode == ViewManager::DrawMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->drawStart(this, e);
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->drawStart(this, e);
+        }
 
     } else if (mode == ViewManager::EraseMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->eraseStart(this, e);
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->eraseStart(this, e);
+        }
+
+        // GF: handle mouse press for NoteEditMode 
+    } else if (mode == ViewManager::NoteEditMode) {
+
+        std::cerr << "mouse pressed in note edit mode" << std::endl;
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->splitStart(this, e); 
+        }
 
     } else if (mode == ViewManager::EditMode) {
 
@@ -1401,33 +1415,33 @@
 //    cerr << "mouseReleaseEvent" << endl;
 
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
-    if (m_manager) mode = m_manager->getToolMode();
+    if (m_manager) mode = m_manager->getToolModeFor(this);
 
     m_releasing = true;
 
     if (m_clickedInRange) {
-	mouseMoveEvent(e);
+        mouseMoveEvent(e);
     }
 
     if (m_navigating || mode == ViewManager::NavigateMode) {
 
-	m_navigating = false;
-
-	if (mode != ViewManager::NavigateMode) {
-	    // restore cursor
-	    toolModeChanged();
-	}
-
-	if (m_shiftPressed) {
-
-	    int x0 = std::min(m_clickPos.x(), m_mousePos.x());
-	    int x1 = std::max(m_clickPos.x(), m_mousePos.x());
-
-	    int y0 = std::min(m_clickPos.y(), m_mousePos.y());
-	    int y1 = std::max(m_clickPos.y(), m_mousePos.y());
+        m_navigating = false;
+
+        if (mode != ViewManager::NavigateMode) {
+            // restore cursor
+            toolModeChanged();
+        }
+
+        if (m_shiftPressed) {
+
+            int x0 = std::min(m_clickPos.x(), m_mousePos.x());
+            int x1 = std::max(m_clickPos.x(), m_mousePos.x());
+
+            int y0 = std::min(m_clickPos.y(), m_mousePos.y());
+            int y1 = std::max(m_clickPos.y(), m_mousePos.y());
 
             zoomToRegion(x0, y0, x1, y1);
-	}
+        }
 
     } else if (mode == ViewManager::SelectMode) {
 
@@ -1436,43 +1450,49 @@
             return;
         }
 
-	if (m_manager && m_manager->haveInProgressSelection()) {
-
-	    bool exclusive;
-	    Selection selection = m_manager->getInProgressSelection(exclusive);
-	    
-	    if (selection.getEndFrame() < selection.getStartFrame() + 2) {
-		selection = Selection();
-	    }
-	    
-	    m_manager->clearInProgressSelection();
-	    
-	    if (exclusive) {
-		m_manager->setSelection(selection);
-	    } else {
-		m_manager->addSelection(selection);
-	    }
-	}
-	
-	update();
+        if (m_manager && m_manager->haveInProgressSelection()) {
+
+            bool exclusive;
+            Selection selection = m_manager->getInProgressSelection(exclusive);
+        
+            if (selection.getEndFrame() < selection.getStartFrame() + 2) {
+                selection = Selection();
+            }
+        
+            m_manager->clearInProgressSelection();
+        
+            if (exclusive) {
+                m_manager->setSelection(selection);
+            } else {
+                m_manager->addSelection(selection);
+            }
+        }
+    
+        update();
 
     } else if (mode == ViewManager::DrawMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->drawEnd(this, e);
-	    update();
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->drawEnd(this, e);
+            update();
+        }
 
     } else if (mode == ViewManager::EraseMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->eraseEnd(this, e);
-	    update();
-	}
-
-    } else if (mode == ViewManager::EditMode) {
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->eraseEnd(this, e);
+            update();
+        }
+
+    } else if (mode == ViewManager::NoteEditMode) {
+    
+        //GF: handle mouse release for NoteEditMode (note: works but will need to re-think this a bit later)
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->splitEnd(this, e);
+            update(); }
 
         if (m_editing) {
             if (!editSelectionEnd(e)) {
@@ -1482,7 +1502,20 @@
                     update();
                 }
             }
-        }
+        } 
+
+    } else if (mode == ViewManager::EditMode) {
+        
+        // GF: edited this previously, but restored to original state
+        if (m_editing) {
+            if (!editSelectionEnd(e)) {
+                Layer *layer = getSelectedLayer();
+                if (layer && layer->isLayerEditable()) {
+                    layer->editEnd(this, e);
+                    update();
+                }
+            }
+        } 
 
     } else if (mode == ViewManager::MeasureMode) {
 
@@ -1525,22 +1558,30 @@
     }
 
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
-    if (m_manager) mode = m_manager->getToolMode();
+    if (m_manager) mode = m_manager->getToolModeFor(this);
 
     QPoint prevPoint = m_identifyPoint;
     m_identifyPoint = e->pos();
 
     if (!m_clickedInRange) {
-	
-	if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) {
-	    bool closeToLeft = false, closeToRight = false;
-	    getSelectionAt(e->x(), closeToLeft, closeToRight);
-	    if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
-		setCursor(Qt::SizeHorCursor);
-	    } else {
-		setCursor(Qt::ArrowCursor);
-	    }
-	}
+    
+        // GF: handle mouse move for context sensitive cursor switching in NoteEditMode.
+        // GF: Propagate the event to FlexiNoteLayer. I somehow feel it's best handeled there rather than here, but perhaps not if this will be needed elsewhere too.
+        if (mode == ViewManager::NoteEditMode && LayerFactory::getInstance()->getLayerType(getTopLayer()) == LayerFactory::FlexiNotes) {
+        
+            dynamic_cast<FlexiNoteLayer *>(getTopLayer())->mouseMoveEvent(this, e);
+
+        }   
+    
+        if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) {
+            bool closeToLeft = false, closeToRight = false;
+            getSelectionAt(e->x(), closeToLeft, closeToRight);
+            if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
+                setCursor(Qt::SizeHorCursor);
+            } else {
+                setCursor(Qt::ArrowCursor);
+            }
+        }
 
         if (!m_manager->isPlaying()) {
 
@@ -1570,17 +1611,17 @@
             }
         }
 
-	return;
+        return;
     }
 
     if (m_navigating || mode == ViewManager::NavigateMode) {
 
-	if (m_shiftPressed) {
-
-	    m_mousePos = e->pos();
-	    update();
-
-	} else {
+        if (m_shiftPressed) {
+
+            m_mousePos = e->pos();
+            update();
+
+        } else {
 
             dragTopLayer(e);
         }
@@ -1593,17 +1634,82 @@
 
     } else if (mode == ViewManager::DrawMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->drawDrag(this, e);
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->drawDrag(this, e);
+        }
 
     } else if (mode == ViewManager::EraseMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    layer->eraseDrag(this, e);
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->eraseDrag(this, e);
+        }
+
+        // GF: handling NoteEditMode dragging and boundary actions for mouseMoveEvent
+    } else if (mode == ViewManager::NoteEditMode) {
+
+        bool resist = true;
+
+        if ((e->modifiers() & Qt::ShiftModifier)) {
+            m_shiftPressed = true;
+        }
+
+        if (m_shiftPressed) resist = false;
+
+        m_dragMode = updateDragMode
+            (m_dragMode,
+             m_clickPos,
+             e->pos(),
+             true,    // can move horiz
+             true,    // can move vert
+             resist,  // resist horiz
+             resist); // resist vert
+
+        if (!m_editing) {
+
+            if (m_dragMode != UnresolvedDrag) {
+
+                m_editing = true;
+
+                QMouseEvent clickEvent(QEvent::MouseButtonPress,
+                                       m_clickPos,
+                                       Qt::NoButton,
+                                       e->buttons(),
+                                       e->modifiers());
+
+                if (!editSelectionStart(&clickEvent)) {
+                    Layer *layer = getSelectedLayer();
+                    if (layer && layer->isLayerEditable()) {
+                        std::cerr << "calling edit start" << std::endl;
+                        layer->editStart(this, &clickEvent);
+                    }
+                }
+            }
+
+        } else {
+
+            if (!editSelectionDrag(e)) {
+
+                Layer *layer = getSelectedLayer();
+
+                if (layer && layer->isLayerEditable()) {
+
+                    int x = e->x();
+                    int y = e->y();
+                    if (m_dragMode == VerticalDrag) x = m_clickPos.x();
+                    else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
+
+                    QMouseEvent moveEvent(QEvent::MouseMove,
+                                          QPoint(x, y),
+                                          Qt::NoButton,
+                                          e->buttons(),
+                                          e->modifiers());
+                    std::cerr << "calling editDrag" << std::endl;
+                    layer->editDrag(this, &moveEvent);
+                }
+            }
+        }
 
     } else if (mode == ViewManager::EditMode) {
 
@@ -1689,18 +1795,18 @@
 Pane::zoomToRegion(int x0, int y0, int x1, int y1)
 {
     int w = x1 - x0;
-	    
+        
     long newStartFrame = getFrameForX(x0);
-	    
+        
     long visibleFrames = getEndFrame() - getStartFrame();
     if (newStartFrame <= -visibleFrames) {
         newStartFrame  = -visibleFrames + 1;
     }
-	    
+        
     if (newStartFrame >= long(getModelsEndFrame())) {
         newStartFrame  = getModelsEndFrame() - 1;
     }
-	    
+        
     float ratio = float(w) / float(width());
 //	cerr << "ratio: " << ratio << endl;
     size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio);
@@ -1785,7 +1891,7 @@
         long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x());
 
         size_t newCentreFrame = m_dragCentreFrame;
-	    
+        
         if (frameOff < 0) {
             newCentreFrame -= frameOff;
         } else if (newCentreFrame >= size_t(frameOff)) {
@@ -1794,7 +1900,7 @@
             newCentreFrame = 0;
         }
 
-#ifdef DEBUG_PANE	    
+#ifdef DEBUG_PANE       
         SVDEBUG << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame <<
             ", models end frame = " << getModelsEndFrame() << endl;
 #endif
@@ -1913,7 +2019,7 @@
     size_t resolution = 1;
     int snapFrameLeft = mouseFrame;
     int snapFrameRight = mouseFrame;
-	
+    
     Layer *layer = getSelectedLayer();
     if (layer && !m_shiftPressed) {
         layer->snapToFeatureFrame(this, snapFrameLeft,
@@ -1926,9 +2032,9 @@
 
     if (snapFrameLeft < 0) snapFrameLeft = 0;
     if (snapFrameRight < 0) snapFrameRight = 0;
-	
+    
     size_t min, max;
-	
+    
     if (m_selectionStartFrame > size_t(snapFrameLeft)) {
         min = snapFrameLeft;
         max = m_selectionStartFrame;
@@ -1994,18 +2100,25 @@
     m_altPressed = (e->modifiers() & Qt::AltModifier);
 
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
-    if (m_manager) mode = m_manager->getToolMode();
+    if (m_manager) mode = m_manager->getToolModeFor(this);
 
     bool relocate = (mode == ViewManager::NavigateMode ||
                      (e->buttons() & Qt::MidButton));
 
+    if (mode == ViewManager::SelectMode) {
+        m_clickedInRange = false;
+        m_manager->clearInProgressSelection();
+        emit doubleClickSelectInvoked(getFrameForX(e->x()));
+        return;
+    }
+
     if (mode == ViewManager::NavigateMode ||
         mode == ViewManager::EditMode) {
 
-	Layer *layer = getSelectedLayer();
-	if (layer && layer->isLayerEditable()) {
-	    if (layer->editOpen(this, e)) relocate = false;
-	}
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            if (layer->editOpen(this, e)) relocate = false;
+        }
 
     } else if (mode == ViewManager::MeasureMode) {
 
@@ -2029,6 +2142,14 @@
             m_dragStartMinValue = dmin;
         }
     }
+    
+    if (mode == ViewManager::NoteEditMode) {
+        std::cerr << "double click in note edit mode" << std::endl;
+        Layer *layer = getSelectedLayer();
+        if (layer && layer->isLayerEditable()) {
+            layer->addNote(this, e); 
+        }
+    }
 
     m_clickedInRange = false; // in case mouseReleaseEvent is not properly called
 }
@@ -2063,31 +2184,31 @@
     int count = e->delta();
 
     if (count > 0) {
-	if (count >= 120) count /= 120;
-	else count = 1;
+    if (count >= 120) count /= 120;
+    else count = 1;
     } 
 
     if (count < 0) {
-	if (count <= -120) count /= 120;
-	else count = -1;
+    if (count <= -120) count /= 120;
+    else count = -1;
     }
 
     if (e->modifiers() & Qt::ControlModifier) {
 
-	// Scroll left or right, rapidly
-
-	if (getStartFrame() < 0 && 
-	    getEndFrame() >= getModelsEndFrame()) return;
-
-	long delta = ((width() / 2) * count * m_zoomLevel);
-
-	if (int(m_centreFrame) < delta) {
-	    setCentreFrame(0);
-	} else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
-	    setCentreFrame(getModelsEndFrame());
-	} else {
-	    setCentreFrame(m_centreFrame - delta);
-	}
+    // Scroll left or right, rapidly
+
+    if (getStartFrame() < 0 && 
+        getEndFrame() >= getModelsEndFrame()) return;
+
+    long delta = ((width() / 2) * count * m_zoomLevel);
+
+    if (int(m_centreFrame) < delta) {
+        setCentreFrame(0);
+    } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
+        setCentreFrame(getModelsEndFrame());
+    } else {
+        setCentreFrame(m_centreFrame - delta);
+    }
 
     } else if (e->modifiers() & Qt::ShiftModifier) {
 
@@ -2107,29 +2228,29 @@
 
     } else {
 
-	// Zoom in or out
-
-	int newZoomLevel = m_zoomLevel;
+    // Zoom in or out
+
+    int newZoomLevel = m_zoomLevel;
   
-	while (count > 0) {
-	    if (newZoomLevel <= 2) {
-		newZoomLevel = 1;
-		break;
-	    }
-	    newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, 
-						      ZoomConstraint::RoundDown);
-	    --count;
-	}
-	
-	while (count < 0) {
-	    newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
-						      ZoomConstraint::RoundUp);
-	    ++count;
-	}
-	
-	if (newZoomLevel != m_zoomLevel) {
-	    setZoomLevel(newZoomLevel);
-	}
+    while (count > 0) {
+        if (newZoomLevel <= 2) {
+        newZoomLevel = 1;
+        break;
+        }
+        newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, 
+                              ZoomConstraint::RoundDown);
+        --count;
+    }
+    
+    while (count < 0) {
+        newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
+                              ZoomConstraint::RoundUp);
+        ++count;
+    }
+    
+    if (newZoomLevel != m_zoomLevel) {
+        setZoomLevel(newZoomLevel);
+    }
     }
 
     emit paneInteractedWith();
@@ -2307,9 +2428,9 @@
 Pane::editSelectionStart(QMouseEvent *e)
 {
     if (!m_identifyFeatures ||
-	!m_manager ||
-	m_manager->getToolMode() != ViewManager::EditMode) {
-	return false;
+        !m_manager ||
+        m_manager->getToolModeFor(this) != ViewManager::EditMode) {
+        return false;
     }
 
     bool closeToLeft, closeToRight;
@@ -2339,8 +2460,8 @@
     Layer *layer = getSelectedLayer();
 
     if (offset == 0 || !layer) {
-	m_editingSelection = Selection();
-	return true;
+        m_editingSelection = Selection();
+        return true;
     }
 
     int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
@@ -2352,25 +2473,25 @@
     Selection newSelection(f0, f1);
     
     if (m_editingSelectionEdge == 0) {
-	
+    
         CommandHistory::getInstance()->startCompoundOperation
             (tr("Drag Selection"), true);
 
-	layer->moveSelection(m_editingSelection, f0);
-	
+        layer->moveSelection(m_editingSelection, f0);
+    
     } else {
-	
+    
         CommandHistory::getInstance()->startCompoundOperation
             (tr("Resize Selection"), true);
 
-	if (m_editingSelectionEdge < 0) {
-	    f1 = m_editingSelection.getEndFrame();
-	} else {
-	    f0 = m_editingSelection.getStartFrame();
-	}
-
-	newSelection = Selection(f0, f1);
-	layer->resizeSelection(m_editingSelection, newSelection);
+        if (m_editingSelectionEdge < 0) {
+            f1 = m_editingSelection.getEndFrame();
+        } else {
+            f0 = m_editingSelection.getStartFrame();
+        }
+
+        newSelection = Selection(f0, f1);
+        layer->resizeSelection(m_editingSelection, newSelection);
     }
     
     m_manager->removeSelection(m_editingSelection);
@@ -2385,7 +2506,7 @@
 void
 Pane::toolModeChanged()
 {
-    ViewManager::ToolMode mode = m_manager->getToolMode();
+    ViewManager::ToolMode mode = m_manager->getToolModeFor(this);
 //    SVDEBUG << "Pane::toolModeChanged(" << mode << ")" << endl;
 
     if (mode == ViewManager::MeasureMode && !m_measureCursor1) {
@@ -2400,33 +2521,38 @@
     switch (mode) {
 
     case ViewManager::NavigateMode:
-	setCursor(Qt::PointingHandCursor);
-	break;
-	
+        setCursor(Qt::PointingHandCursor);
+        break;
+    
     case ViewManager::SelectMode:
-	setCursor(Qt::ArrowCursor);
-	break;
-	
+        setCursor(Qt::ArrowCursor);
+        break;
+    
     case ViewManager::EditMode:
-	setCursor(Qt::UpArrowCursor);
-	break;
-	
+        setCursor(Qt::UpArrowCursor);
+        break;
+    
     case ViewManager::DrawMode:
-	setCursor(Qt::CrossCursor);
-	break;
-	
+        setCursor(Qt::CrossCursor);
+        break;
+    
     case ViewManager::EraseMode:
-	setCursor(Qt::CrossCursor);
-	break;
+        setCursor(Qt::CrossCursor);
+        break;
 
     case ViewManager::MeasureMode:
         if (m_measureCursor1) setCursor(*m_measureCursor1);
-	break;
-
-/*	
+        break;
+
+        // GF: NoteEditMode uses the same default cursor as EditMode, but it will change in a context sensitive manner.
+    case ViewManager::NoteEditMode:
+        setCursor(Qt::UpArrowCursor);
+        break;
+
+/*  
     case ViewManager::TextMode:
-	setCursor(Qt::IBeamCursor);
-	break;
+    setCursor(Qt::IBeamCursor);
+    break;
 */
     }
 }
@@ -2510,7 +2636,7 @@
     }
 
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
-    if (m_manager) mode = m_manager->getToolMode();
+    if (m_manager) mode = m_manager->getToolModeFor(this);
 
     bool editable = false;
     Layer *layer = getSelectedLayer();
@@ -2562,21 +2688,21 @@
     } else if (mode == ViewManager::DrawMode) {
         
         //!!! could call through to a layer function to find out exact meaning
-	if (editable) {
+        if (editable) {
             help = tr("Click to add a new item in the active layer");
         }
 
     } else if (mode == ViewManager::EraseMode) {
         
         //!!! could call through to a layer function to find out exact meaning
-	if (editable) {
+        if (editable) {
             help = tr("Click to erase an item from the active layer");
         }
         
     } else if (mode == ViewManager::EditMode) {
         
         //!!! could call through to layer
-	if (editable) {
+        if (editable) {
             help = tr("Click and drag an item in the active layer to move it; hold Shift to override initial resistance");
             if (pos) {
                 bool closeToLeft = false, closeToRight = false;
@@ -2620,8 +2746,8 @@
 {
     View::toXml
         (stream, indent,
-	 QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
-	 .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
+     QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
+     .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
 }
 
 
--- a/view/Pane.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/Pane.h	Tue Jan 28 15:02:09 2014 +0000
@@ -67,6 +67,7 @@
     void rightButtonMenuRequested(QPoint position);
     void dropAccepted(QStringList uriList);
     void dropAccepted(QString text);
+    void doubleClickSelectInvoked(size_t frame);
 
 public slots:
     virtual void toolModeChanged();
--- a/view/PaneStack.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/PaneStack.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -39,6 +39,7 @@
 PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) :
     QFrame(parent),
     m_currentPane(0),
+    m_showAccessories(true),
     m_splitter(new QSplitter),
     m_propertyStackStack(new QStackedWidget),
     m_viewManager(viewManager),
@@ -60,6 +61,12 @@
     setLayout(layout);
 }
 
+void
+PaneStack::setShowPaneAccessories(bool show)
+{
+    m_showAccessories = show;
+}
+
 Pane *
 PaneStack::addPane(bool suppressPropertyBox)
 {
@@ -79,6 +86,7 @@
     xButton->setIcon(IconLoader().load("cross"));
     xButton->setFixedSize(QSize(16, 16));
     xButton->setFlat(true);
+    xButton->setVisible(m_showAccessories);
     layout->addWidget(xButton, 0, 0);
     connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked()));
 
@@ -88,6 +96,7 @@
     layout->setRowStretch(1, 20);
     currentIndicator->setMinimumWidth(8);
     currentIndicator->setScaledContents(true);
+    currentIndicator->setVisible(m_showAccessories);
 
     long initialCentreFrame = -1;
     for (int i = 0; i < m_panes.size(); ++i) {
@@ -149,6 +158,8 @@
             this, SLOT(paneDropAccepted(QStringList)));
     connect(pane, SIGNAL(dropAccepted(QString)),
             this, SLOT(paneDropAccepted(QString)));
+    connect(pane, SIGNAL(doubleClickSelectInvoked(size_t)),
+            this, SIGNAL(doubleClickSelectInvoked(size_t)));
 
     emit paneAdded(pane);
     emit paneAdded();
@@ -288,8 +299,8 @@
     bool multi = (getPaneCount() > 1);
     for (std::vector<PaneRec>::iterator i = m_panes.begin();
          i != m_panes.end(); ++i) {
-        i->xButton->setVisible(multi);
-        i->currentIndicator->setVisible(multi);
+        i->xButton->setVisible(multi && m_showAccessories);
+        i->currentIndicator->setVisible(multi && m_showAccessories);
     }
 }
 
@@ -580,24 +591,44 @@
 
     int count = sizes.size();
 
-    int total = 0;
+    int fixed = 0, variable = 0, total = 0;
+    int varicount = 0;
+
     for (int i = 0; i < count; ++i) {
         total += sizes[i];
     }
 
+    variable = total;
+
+    for (int i = 0; i < count; ++i) {
+        int minh = m_panes[i].pane->minimumSize().height();
+        if (minh == m_panes[i].pane->maximumSize().height()) {
+            fixed += minh;
+            variable -= minh;
+        } else {
+            varicount++;
+        }
+    }
+
     if (total == 0) return;
 
     sizes.clear();
 
-    int each = total / count;
+    int each = (varicount > 0 ? (variable / varicount) : 0);
     int remaining = total;
 
     for (int i = 0; i < count; ++i) {
         if (i == count - 1) {
             sizes.push_back(remaining);
         } else {
-            sizes.push_back(each);
-            remaining -= each;
+            int minh = m_panes[i].pane->minimumSize().height();
+            if (minh == m_panes[i].pane->maximumSize().height()) {
+                sizes.push_back(minh);
+                remaining -= minh;
+            } else {
+                sizes.push_back(each);
+                remaining -= each;
+            }
         }
     }
 
@@ -612,4 +643,3 @@
     m_splitter->setSizes(sizes);
 }
 
-
--- a/view/PaneStack.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/PaneStack.h	Tue Jan 28 15:02:09 2014 +0000
@@ -68,6 +68,8 @@
     void setLayoutStyle(LayoutStyle style);
 
     void setPropertyStackMinWidth(int mw);
+    
+    void setShowPaneAccessories(bool show); // current indicator, close button
 
     void sizePanesEqually();
 
@@ -91,6 +93,8 @@
 
     void paneDeleteButtonClicked(Pane *pane);
 
+    void doubleClickSelectInvoked(size_t frame);
+
 public slots:
     void propertyContainerAdded(PropertyContainer *);
     void propertyContainerRemoved(PropertyContainer *);
@@ -119,6 +123,8 @@
     std::vector<PaneRec> m_panes;
     std::vector<PaneRec> m_hiddenPanes;
 
+    bool m_showAccessories;
+
     QSplitter *m_splitter;
     QStackedWidget *m_propertyStackStack;
 
--- a/view/ViewManager.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/ViewManager.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -360,9 +360,32 @@
     case DrawMode: emit activity(tr("Enter Draw mode")); break;
     case EraseMode: emit activity(tr("Enter Erase mode")); break;
     case MeasureMode: emit activity(tr("Enter Measure mode")); break;
+    case NoteEditMode: emit activity(tr("Enter NoteEdit mode")); break; // GF: NoteEditMode activity (I'm not yet certain why we need to emit this.)
     };
 }
 
+ViewManager::ToolMode
+ViewManager::getToolModeFor(const View *v) const
+{
+    if (m_toolModeOverrides.find(v) == m_toolModeOverrides.end()) {
+        return getToolMode();
+    } else {
+        return m_toolModeOverrides.find(v)->second;
+    }
+}
+
+void
+ViewManager::setToolModeFor(const View *v, ToolMode mode)
+{
+    m_toolModeOverrides[v] = mode;
+}
+
+void
+ViewManager::clearToolModeOverrides()
+{
+    m_toolModeOverrides.clear();
+}
+
 void
 ViewManager::setPlayLoopMode(bool mode)
 {
--- a/view/ViewManager.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/view/ViewManager.h	Tue Jan 28 15:02:09 2014 +0000
@@ -101,11 +101,19 @@
         EditMode,
 	DrawMode,
 	EraseMode,
-	MeasureMode
+	MeasureMode,
+	NoteEditMode //GF: Tonioni: this tool mode will be context sensitive.
     };
     ToolMode getToolMode() const { return m_toolMode; }
     void setToolMode(ToolMode mode);
 
+    /// Override the tool mode for a specific view 
+    void setToolModeFor(const View *v, ToolMode mode);
+    /// Return override mode if it exists for this view or global mode otherwise
+    ToolMode getToolModeFor(const View *v) const;
+    /// Clear all current view-specific overrides
+    void clearToolModeOverrides();
+
     bool getPlayLoopMode() const { return m_playLoopMode; }
     void setPlayLoopMode(bool on);
 
@@ -273,6 +281,7 @@
     Clipboard m_clipboard;
 
     ToolMode m_toolMode;
+    std::map<const View *, ToolMode> m_toolModeOverrides;
 
     bool m_playLoopMode;
     bool m_playSelectionMode;
--- a/view/view.pro	Wed Dec 04 14:00:27 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-TEMPLATE = lib
-
-SV_UNIT_PACKAGES =
-load(../prf/sv.prf)
-
-CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
-QT += xml
-
-TARGET = svview
-
-DEPENDPATH += . ..
-INCLUDEPATH += . ..
-OBJECTS_DIR = tmp_obj
-MOC_DIR = tmp_moc
-
-# Input
-HEADERS += Overview.h \
-           Pane.h \
-           PaneStack.h \
-           View.h \
-           ViewManager.h
-SOURCES += Overview.cpp \
-           Pane.cpp \
-           PaneStack.cpp \
-           View.cpp \
-           ViewManager.cpp
--- a/widgets/InteractiveFileFinder.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/widgets/InteractiveFileFinder.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -81,6 +81,20 @@
             .arg(RDFImporter::getKnownExtensions());
         break;
 
+    case LayerFileNonSV:
+        settingsKey = "layerpath";
+        filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)")
+            .arg(DataFileReaderFactory::getKnownExtensions())
+            .arg(RDFImporter::getKnownExtensions());
+        break;
+
+    case LayerFileNoMidiNonSV:
+        settingsKey = "layerpath";
+        filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)")
+            .arg(DataFileReaderFactory::getKnownExtensions())
+            .arg(RDFImporter::getKnownExtensions());
+        break;
+
     case SessionOrAudioFile:
         settingsKey = "lastpath";
         filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)")
@@ -224,6 +238,18 @@
         filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions());
         break;
 
+    case LayerFileNonSV:
+        settingsKey = "savelayerpath";
+        title = tr("Select a file to export to");
+        filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions());
+        break;
+
+    case LayerFileNoMidiNonSV:
+        settingsKey = "savelayerpath";
+        title = tr("Select a file to export to");
+        filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions());
+        break;
+
     case SessionOrAudioFile:
         cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << endl;
         abort();
@@ -299,7 +325,8 @@
 
         cerr << "type = " << type << ", suffix = " << fi.suffix() << endl;
         
-        if ((type == LayerFile || type == LayerFileNoMidi)
+        if ((type == LayerFile || type == LayerFileNoMidi || 
+             type == LayerFileNonSV || type == LayerFileNoMidiNonSV)
             && fi.suffix() == "") {
             QString expectedExtension;
             QString selectedFilter = dialog.selectedNameFilter();
@@ -369,6 +396,14 @@
         settingsKey = "layerpath";
         break;
 
+    case LayerFileNonSV:
+        settingsKey = "layerpath";
+        break;
+
+    case LayerFileNoMidiNonSV:
+        settingsKey = "layerpath";
+        break;
+
     case SessionOrAudioFile:
         settingsKey = "lastpath";
         break;
--- a/widgets/PropertyBox.cpp	Wed Dec 04 14:00:27 2013 +0000
+++ b/widgets/PropertyBox.cpp	Tue Jan 28 15:02:09 2014 +0000
@@ -188,6 +188,7 @@
 
 	layout->insertStretch(-1, 10);
 
+/*!!! todo: restore playback sample selection
         if (params->getPlayPluginId() != "") {
             QPushButton *pluginButton = new QPushButton(QIcon(":icons/faders.png"), "");
             pluginButton->setFixedWidth(24);
@@ -196,6 +197,7 @@
             connect(pluginButton, SIGNAL(clicked()),
                     this, SLOT(editPlugin()));
         }
+*/
 
 	AudioDial *gainDial = new AudioDial;
 	layout->addWidget(gainDial);
@@ -742,7 +744,7 @@
 
     updateContextHelp(obj);
 }
-
+/*!!! todo: restore playback sample selection
 void
 PropertyBox::editPlugin()
 {
@@ -794,7 +796,7 @@
 
     params->setPlayPluginConfiguration(configurationXml);
 }    
-
+*/
 void
 PropertyBox::layerVisibilityChanged(bool visible)
 {
--- a/widgets/PropertyBox.h	Wed Dec 04 14:00:27 2013 +0000
+++ b/widgets/PropertyBox.h	Tue Jan 28 15:02:09 2014 +0000
@@ -47,7 +47,7 @@
 public slots:
     void propertyContainerPropertyChanged(PropertyContainer *);
     void propertyContainerPropertyRangeChanged(PropertyContainer *);
-    void pluginConfigurationChanged(QString);
+//!!!    void pluginConfigurationChanged(QString);
     void layerVisibilityChanged(bool);
 
 protected slots:
@@ -66,7 +66,7 @@
     void unitDatabaseChanged();
     void colourDatabaseChanged();
 
-    void editPlugin();
+//!!!    void editPlugin();
 
     void mouseEnteredWidget();
     void mouseLeftWidget();
--- a/widgets/widgets.pro	Wed Dec 04 14:00:27 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-TEMPLATE = lib
-
-SV_UNIT_PACKAGES = vamp-hostsdk fftw3f
-load(../prf/sv.prf)
-
-CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
-QT += xml
-
-TARGET = svwidgets
-
-DEPENDPATH += . ..
-INCLUDEPATH += . ..
-OBJECTS_DIR = tmp_obj
-MOC_DIR = tmp_moc
-
-# Input
-HEADERS += ActivityLog.h \
-           AudioDial.h \
-           ClickableLabel.h \
-           ColourNameDialog.h \
-           CommandHistory.h \
-           CSVFormatDialog.h \
-           Fader.h \
-           InteractiveFileFinder.h \
-           IconLoader.h \
-           ImageDialog.h \
-           ItemEditDialog.h \
-           KeyReference.h \
-           LabelCounterInputDialog.h \
-           LayerTree.h \
-           LayerTreeDialog.h \
-           LEDButton.h \
-           ListInputDialog.h \
-           MIDIFileImportDialog.h \
-           ModelDataTableDialog.h \
-           NotifyingCheckBox.h \
-           NotifyingComboBox.h \
-           NotifyingPushButton.h \
-           NotifyingTabBar.h \
-           Panner.h \
-           PluginParameterBox.h \
-           PluginParameterDialog.h \
-           ProgressDialog.h \
-           PropertyBox.h \
-           PropertyStack.h \
-           RangeInputDialog.h \
-           SelectableLabel.h \
-           SubdividingMenu.h \
-           TextAbbrev.h \
-           Thumbwheel.h \
-           TipDialog.h \
-           TransformFinder.h \
-           WindowShapePreview.h \
-           WindowTypeSelector.h
-SOURCES += ActivityLog.cpp \
-           AudioDial.cpp \
-           ColourNameDialog.cpp \
-           CommandHistory.cpp \
-           CSVFormatDialog.cpp \
-           Fader.cpp \
-           InteractiveFileFinder.cpp \
-           IconLoader.cpp \
-           ImageDialog.cpp \
-           ItemEditDialog.cpp \
-           KeyReference.cpp \
-           LabelCounterInputDialog.cpp \
-           LayerTree.cpp \
-           LayerTreeDialog.cpp \
-           LEDButton.cpp \
-           ListInputDialog.cpp \
-           MIDIFileImportDialog.cpp \
-           ModelDataTableDialog.cpp \
-           NotifyingCheckBox.cpp \
-           NotifyingComboBox.cpp \
-           NotifyingPushButton.cpp \
-           NotifyingTabBar.cpp \
-           Panner.cpp \
-           PluginParameterBox.cpp \
-           PluginParameterDialog.cpp \
-           ProgressDialog.cpp \
-           PropertyBox.cpp \
-           PropertyStack.cpp \
-           RangeInputDialog.cpp \
-           SelectableLabel.cpp \
-           SubdividingMenu.cpp \
-           TextAbbrev.cpp \
-           Thumbwheel.cpp \
-           TipDialog.cpp \
-           TransformFinder.cpp \
-           WindowShapePreview.cpp \
-           WindowTypeSelector.cpp