changeset 694:ad12e428785b tonioni

Merge from default branch
author Chris Cannam
date Tue, 03 Dec 2013 17:58:40 +0000
parents 212644efa523 (diff) 81c4c44ecc10 (current diff)
children 6d9624e0ac55
files layer/FlexiNoteLayer.cpp layer/FlexiNoteLayer.h layer/NoteLayer.cpp svgui.pro view/ViewManager.cpp view/ViewManager.h
diffstat 12 files changed, 2285 insertions(+), 270 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/FlexiNoteLayer.cpp	Tue Dec 03 17:58:40 2013 +0000
@@ -0,0 +1,1647 @@
+/* -*- 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 "data/model/FlexiNoteModel.h"
+
+#include "widgets/ItemEditDialog.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);
+}
+
+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
+                (m_model->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 = m_model->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 = m_model->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 (m_model->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(m_model->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) {
+
+    if (snap == SnapRight) {
+
+        if (i->frame > frame) {
+        snapped = i->frame;
+        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;
+        }
+    }
+    }
+
+    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 = m_model->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
+NoteLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
+{
+    return 10;
+}
+
+void
+NoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+{
+    float fmin, fmax;
+    getDisplayExtents(fmin, fmax);
+    PianoScale().paintPianoVertical
+        (v, paint, QRect(0, 0, 10, v->height()), fmin, fmax);
+    paint.drawLine(10, 0, 10, v->height());
+}
+
+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,
+         m_model->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 Dec 03 17:58:40 2013 +0000
@@ -0,0 +1,197 @@
+/* -*- 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 "data/model/FlexiNoteModel.h"
+
+#include <QObject>
+#include <QColor>
+
+class View;
+class QPainter;
+
+class FlexiNoteLayer : public SingleColourLayer
+{
+    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);
+
+protected:
+    void getScaleExtents(View *, float &min, float &max, bool &log) const;
+    int getYForValue(View *v, float value) const;
+    float getValueForY(View *v, int y) 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	Tue Dec 03 17:56:38 2013 +0000
+++ b/layer/Layer.h	Tue Dec 03 17:58:40 2013 +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	Tue Dec 03 17:56:38 2013 +0000
+++ b/layer/LayerFactory.cpp	Tue Dec 03 17:58:40 2013 +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	Tue Dec 03 17:56:38 2013 +0000
+++ b/layer/LayerFactory.h	Tue Dec 03 17:58:40 2013 +0000
@@ -35,6 +35,7 @@
 	TimeInstants,
 	TimeValues,
 	Notes,
+	FlexiNotes,
 	Regions,
 	Text,
         Image,
--- a/layer/NoteLayer.cpp	Tue Dec 03 17:56:38 2013 +0000
+++ b/layer/NoteLayer.cpp	Tue Dec 03 17:58:40 2013 +0000
@@ -52,12 +52,12 @@
     m_scaleMinimum(0),
     m_scaleMaximum(0)
 {
-    
+  	SVDEBUG << "constructed NoteLayer" << endl;
 }
 
 void
 NoteLayer::setModel(NoteModel *model)
-{
+{	
     if (m_model == model) return;
     m_model = model;
 
--- a/layer/layer.pro	Tue Dec 03 17:56:38 2013 +0000
+++ b/layer/layer.pro	Tue Dec 03 17:58:40 2013 +0000
@@ -22,6 +22,7 @@
            Layer.h \
            LayerFactory.h \
            NoteLayer.h \
+           FlexiNoteLayer.h \
            PaintAssistant.h \
            RegionLayer.h \
            SingleColourLayer.h \
@@ -42,6 +43,7 @@
            Layer.cpp \
            LayerFactory.cpp \
            NoteLayer.cpp \
+           FlexiNoteLayer.cpp \
            PaintAssistant.cpp \
            RegionLayer.cpp \
            SingleColourLayer.cpp \
--- a/svgui.pro	Tue Dec 03 17:56:38 2013 +0000
+++ b/svgui.pro	Tue Dec 03 17:58:40 2013 +0000
@@ -35,6 +35,7 @@
            layer/Layer.h \
            layer/LayerFactory.h \
            layer/NoteLayer.h \
+           layer/FlexiNoteLayer.h \
            layer/PaintAssistant.h \
            layer/PianoScale.h \
            layer/RegionLayer.h \
@@ -56,6 +57,7 @@
            layer/Layer.cpp \
            layer/LayerFactory.cpp \
            layer/NoteLayer.cpp \
+           layer/FlexiNoteLayer.cpp \
            layer/PaintAssistant.cpp \
            layer/PianoScale.cpp \
            layer/RegionLayer.cpp \
--- a/view/Pane.cpp	Tue Dec 03 17:56:38 2013 +0000
+++ b/view/Pane.cpp	Tue Dec 03 17:58:40 2013 +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);
@@ -331,16 +336,16 @@
     if (m_manager && m_manager->getToolMode() == 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->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;
+        }
+    }
     }
 
     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;
 }
@@ -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);
@@ -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) {
 
@@ -1406,28 +1420,28 @@
     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) {
 
@@ -1531,16 +1564,24 @@
     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,40 +1611,105 @@
             }
         }
 
-	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);
         }
 
     } else if (mode == ViewManager::SelectMode) {
 
-        if (!hasTopLayerTimeXAxis()) return;
-
-        dragExtendSelection(e);
+            if (!hasTopLayerTimeXAxis()) return;
+
+            dragExtendSelection(e);
 
     } 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;
@@ -2002,10 +2108,10 @@
     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 +2135,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 +2177,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 +2221,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 +2421,9 @@
 Pane::editSelectionStart(QMouseEvent *e)
 {
     if (!m_identifyFeatures ||
-	!m_manager ||
-	m_manager->getToolMode() != ViewManager::EditMode) {
-	return false;
+    !m_manager ||
+    m_manager->getToolMode() != ViewManager::EditMode) {
+    return false;
     }
 
     bool closeToLeft, closeToRight;
@@ -2339,8 +2453,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 +2466,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);
@@ -2400,33 +2514,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;
 */
     }
 }
@@ -2562,21 +2681,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 +2739,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/PaneStack.cpp	Tue Dec 03 17:56:38 2013 +0000
+++ b/view/PaneStack.cpp	Tue Dec 03 17:58:40 2013 +0000
@@ -580,24 +580,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 +632,3 @@
     m_splitter->setSizes(sizes);
 }
 
-
--- a/view/ViewManager.cpp	Tue Dec 03 17:56:38 2013 +0000
+++ b/view/ViewManager.cpp	Tue Dec 03 17:58:40 2013 +0000
@@ -360,6 +360,7 @@
     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.)
     };
 }
 
--- a/view/ViewManager.h	Tue Dec 03 17:56:38 2013 +0000
+++ b/view/ViewManager.h	Tue Dec 03 17:58:40 2013 +0000
@@ -98,10 +98,11 @@
     enum ToolMode {
 	NavigateMode,
 	SelectMode,
-        EditMode,
+    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);