Mercurial > hg > svgui
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 ¬e) 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 ¬e, 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 ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const; + void updateNoteValue(View *v, FlexiNoteModel::Point ¬e) 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