# HG changeset patch # User Chris Cannam # Date 1400057674 -3600 # Node ID a964151832a766829dff00b709262910ebac4fcc # Parent 8b614632568c353c01d2a7e5c48ee9bb98a4ecea# Parent 734ee80286c33f445b0584e077344a7e3862e1a2 Merge from branch tony_integration diff -r 8b614632568c -r a964151832a7 layer/FlexiNoteLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/FlexiNoteLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -0,0 +1,1824 @@ +/* -*- 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 +#include +#include +#include +#include + +#include +#include +#include +#include // GF: included to compile std::numerical_limits on linux +#include + + +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::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 << "editStart: mode is " << m_editMode << ", 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; + } + } + updateNoteValue(v, m_editingPoint); + 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; + } + + long frame = v->getFrameForX(e->x()); + + splitNotesAt(v, frame, e); +} + +void +FlexiNoteLayer::splitNotesAt(View *v, int frame) +{ + splitNotesAt(v, frame, 0); +} + +void +FlexiNoteLayer::splitNotesAt(View *v, int frame, QMouseEvent *e) +{ + FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + if (onPoints.empty()) return; + + FlexiNote note(*onPoints.begin()); + + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Edit Point")); + command->deletePoint(note); + + if (!e || !(e->modifiers() & Qt::ShiftModifier)) { + + int gap = 0; // MM: I prefer a gap of 0, but we can decide later + + 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) { + if (updateNoteValue(v, newNote1)) { + command->addPoint(newNote1); + } + if (updateNoteValue(v, newNote2)) { + command->addPoint(newNote2); + } + } else { + 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); + } +} + +SparseTimeValueModel * +FlexiNoteLayer::getAssociatedPitchModel(View *v) const +{ + // Better than we used to do, but still not very satisfactory + + cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl; + + for (int i = 0; i < v->getLayerCount(); ++i) { + Layer *layer = v->getLayer(i); + if (layer && !layer->isLayerDormant(v) && + layer->getLayerPresentationName() != "candidate") { + cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl; + SparseTimeValueModel *model = qobject_cast + (layer->getModel()); + cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl; + if (model && model->getScaleUnits() == "Hz") { + cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl; + return model; + } + } + } + return 0; +} + +void +FlexiNoteLayer::snapSelectedNotesToPitchTrack(View *v, Selection s) +{ + if (!m_model) return; + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Snap Notes")); + + cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl; + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + + FlexiNote note(*i); + + cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.frame << " to " << note.frame + note.duration << endl; + + if (!s.contains(note.frame) && + !s.contains(note.frame + note.duration - 1)) { + continue; + } + + FlexiNote newNote(note); + + command->deletePoint(note); + + + } + + finish(command); +} + +void +FlexiNoteLayer::mergeNotes(View *v, Selection s, bool inclusive) +{ + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + FlexiNoteModel::PointList::iterator i = points.begin(); + if (inclusive) { + while (i != points.end() && i->frame + i->duration < s.getStartFrame()) { + ++i; + } + } else { + while (i != points.end() && i->frame < s.getStartFrame()) { + ++i; + } + } + + if (i == points.end()) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Merge Notes")); + + FlexiNote newNote(*i); + + while (i != points.end()) { + + if (inclusive) { + if (i->frame >= s.getEndFrame()) break; + } else { + if (i->frame + i->duration > s.getEndFrame()) break; + } + + newNote.duration = i->frame + i->duration - newNote.frame; + command->deletePoint(*i); + + ++i; + } + + updateNoteValue(v, newNote); + command->addPoint(newNote); + finish(command); +} + +bool +FlexiNoteLayer::updateNoteValue(View *v, FlexiNoteModel::Point ¬e) const +{ + SparseTimeValueModel *model = getAssociatedPitchModel(v); + if (!model) return false; + + std::cerr << model->getTypeName() << std::endl; + + SparseModel::PointList dataPoints = + model->getPoints(note.frame, note.frame + note.duration); + + std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl; + + if (dataPoints.empty()) return false; + + std::vector pitchValues; + + for (SparseModel::PointList::const_iterator i = + dataPoints.begin(); i != dataPoints.end(); ++i) { + if (i->frame >= note.frame && + i->frame < note.frame + note.duration) { + pitchValues.push_back(i->value); + } + } + + if (pitchValues.empty()) return false; + + 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; + + return true; +} + +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::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. + } +} + + diff -r 8b614632568c -r a964151832a7 layer/FlexiNoteLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/FlexiNoteLayer.h Wed May 14 09:54:34 2014 +0100 @@ -0,0 +1,210 @@ +/* -*- 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 +#include + +class View; +class QPainter; +class SparseTimeValueModel; + +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); + + void splitNotesAt(View *v, int frame); + void snapSelectedNotesToPitchTrack(View *v, Selection s); + void mergeNotes(View *v, Selection s, bool inclusive); + + 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; + SparseTimeValueModel *getAssociatedPitchModel(View *v) const; + bool updateNoteValue(View *v, FlexiNoteModel::Point ¬e) const; + void splitNotesAt(View *v, int frame, QMouseEvent *e); + + 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 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 + diff -r 8b614632568c -r a964151832a7 layer/Layer.h --- a/layer/Layer.h Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/Layer.h Wed May 14 09:54:34 2014 +0100 @@ -131,7 +131,7 @@ virtual int getVerticalScaleWidth(View *, bool detailed, QPainter &) const = 0; - virtual void paintVerticalScale(View *, bool detailed, + virtual void paintVerticalScale(View *, bool /* detailed */, QPainter &, QRect) const { } virtual bool getCrosshairExtents(View *, QPainter &, QPoint /* cursorPos */, @@ -150,7 +150,7 @@ return ""; } - virtual QString getLabelPreceding(size_t frame) const { + virtual QString getLabelPreceding(size_t /* frame */) const { return ""; } @@ -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 *, QMouseEvent *) { }; + // Measurement rectangle (or equivalent). Unlike draw and edit, // the base Layer class can provide working implementations of // these for most situations. diff -r 8b614632568c -r a964151832a7 layer/LayerFactory.cpp --- a/layer/LayerFactory.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/LayerFactory.cpp Wed May 14 09:54:34 2014 +0100 @@ -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(model)) { + types.insert(FlexiNotes); + } + if (dynamic_cast(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(layer)) return TimeRuler; if (dynamic_cast(layer)) return TimeInstants; if (dynamic_cast(layer)) return TimeValues; + if (dynamic_cast(layer)) return FlexiNotes; if (dynamic_cast(layer)) return Notes; if (dynamic_cast(layer)) return Regions; if (dynamic_cast(layer)) return Text; @@ -225,6 +235,7 @@ case TimeInstants: return "instants"; case TimeValues: return "values"; case Notes: return "notes"; + case FlexiNotes: return "flexinotes"; 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(layer, model)) // return; - + if (trySetModel(layer, model)) return; @@ -301,9 +313,13 @@ if (trySetModel(layer, model)) return; - if (trySetModel(layer, model)) - return; + if (trySetModel(layer, model)) + return; + // GF: added FlexiNoteLayer + if (trySetModel(layer, model)) + return; + if (trySetModel(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; diff -r 8b614632568c -r a964151832a7 layer/LayerFactory.h --- a/layer/LayerFactory.h Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/LayerFactory.h Wed May 14 09:54:34 2014 +0100 @@ -35,6 +35,7 @@ TimeInstants, TimeValues, Notes, + FlexiNotes, Regions, Text, Image, diff -r 8b614632568c -r a964151832a7 layer/NoteLayer.cpp --- a/layer/NoteLayer.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/NoteLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -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; diff -r 8b614632568c -r a964151832a7 layer/RegionLayer.cpp --- a/layer/RegionLayer.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/RegionLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -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; diff -r 8b614632568c -r a964151832a7 layer/ShowLayerCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ShowLayerCommand.h Wed May 14 09:54:34 2014 +0100 @@ -0,0 +1,42 @@ +/* -*- 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 _SHOW_LAYER_COMMAND_H_ +#define _SHOW_LAYER_COMMAND_H_ + +#include "base/Command.h" + +class ShowLayerCommand : public Command +{ +public: + ShowLayerCommand(View *view, Layer *layer, bool show, QString commandName) : + m_view(view), m_layer(layer), m_show(show), m_name(commandName) { } + void execute() { + m_layer->showLayer(m_view, m_show); + } + void unexecute() { + m_layer->showLayer(m_view, !m_show); + } + QString getName() const { + return m_name; + } +protected: + View *m_view; + Layer *m_layer; + bool m_show; + QString m_name; +}; + +#endif diff -r 8b614632568c -r a964151832a7 layer/SpectrogramLayer.cpp --- a/layer/SpectrogramLayer.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/SpectrogramLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -3657,18 +3657,21 @@ "colourScheme=\"%4\" " "colourRotation=\"%5\" " "frequencyScale=\"%6\" " - "binDisplay=\"%7\" " - "normalizeColumns=\"%8\" " - "normalizeVisibleArea=\"%9\"") + "binDisplay=\"%7\" ") .arg(m_minFrequency) .arg(m_maxFrequency) .arg(m_colourScale) .arg(m_colourMap) .arg(m_colourRotation) .arg(m_frequencyScale) - .arg(m_binDisplay) + .arg(m_binDisplay); + + s += QString("normalizeColumns=\"%1\" " + "normalizeVisibleArea=\"%2\" " + "normalizeHybrid=\"%3\" ") .arg(m_normalizeColumns ? "true" : "false") - .arg(m_normalizeVisibleArea ? "true" : "false"); + .arg(m_normalizeVisibleArea ? "true" : "false") + .arg(m_normalizeHybrid ? "true" : "false"); Layer::toXml(stream, indent, extraAttributes + " " + s); } @@ -3741,5 +3744,9 @@ bool normalizeVisibleArea = (attributes.value("normalizeVisibleArea").trimmed() == "true"); setNormalizeVisibleArea(normalizeVisibleArea); + + bool normalizeHybrid = + (attributes.value("normalizeHybrid").trimmed() == "true"); + setNormalizeHybrid(normalizeHybrid); } diff -r 8b614632568c -r a964151832a7 layer/TimeValueLayer.cpp --- a/layer/TimeValueLayer.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/TimeValueLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -972,7 +972,7 @@ i != points.end(); ++i) { if (m_derivative && i == points.begin()) continue; - + const SparseTimeValueModel::Point &p(*i); float value = p.value; @@ -987,6 +987,10 @@ bool gap = false; if (m_plotStyle == PlotDiscreteCurves) { + if (value == 0.0) { + // Treat zeros as gaps + continue; + } gap = (p.frame > prevFrame && (p.frame - prevFrame >= m_model->getResolution() * 2)); } @@ -1000,6 +1004,7 @@ } bool haveNext = false; + float nvalue = 0.f; int nf = v->getModelsEndFrame(); int nx = v->getXForFrame(nf); int ny = y; @@ -1009,7 +1014,7 @@ if (j != points.end()) { const SparseTimeValueModel::Point &q(*j); - float nvalue = q.value; + nvalue = q.value; if (m_derivative) nvalue -= p.value; nf = q.frame; nx = v->getXForFrame(nf); @@ -1116,7 +1121,9 @@ float y1 = ny; if (m_plotStyle == PlotDiscreteCurves) { - bool nextGap = nf - p.frame >= m_model->getResolution() * 2; + bool nextGap = + (nvalue == 0.0) || + (nf - p.frame >= m_model->getResolution() * 2); if (nextGap) { x1 = x0; y1 = y0; @@ -1163,34 +1170,37 @@ paint.drawRect(x, -1, nx - x, v->height() + 1); } - QString label = p.label; - bool italic = false; + if (v->shouldShowFeatureLabels()) { - if (label == "" && - (m_plotStyle == PlotPoints || - m_plotStyle == PlotSegmentation || - m_plotStyle == PlotConnectedPoints)) { - char lc[20]; - snprintf(lc, 20, "%.3g", p.value); - label = lc; - italic = true; + QString label = p.label; + bool italic = false; + + if (label == "" && + (m_plotStyle == PlotPoints || + m_plotStyle == PlotSegmentation || + m_plotStyle == PlotConnectedPoints)) { + char lc[20]; + snprintf(lc, 20, "%.3g", p.value); + label = lc; + italic = true; + } + + if (label != "") { + // Quick test for 20px before we do the slower test using metrics + bool haveRoom = (nx > x + 20); + haveRoom = (haveRoom && + (nx > x + 6 + paint.fontMetrics().width(label))); + if (haveRoom || + (!haveNext && + (pointCount == 0 || !italic))) { + v->drawVisibleText(paint, x + 5, textY, label, + italic ? + View::OutlinedItalicText : + View::OutlinedText); + } + } } - if (label != "") { - // Quick test for 20px before we do the slower test using metrics - bool haveRoom = (nx > x + 20); - haveRoom = (haveRoom && - (nx > x + 6 + paint.fontMetrics().width(label))); - if (haveRoom || - (!haveNext && - (pointCount == 0 || !italic))) { - v->drawVisibleText(paint, x + 5, textY, label, - italic ? - View::OutlinedItalicText : - View::OutlinedText); - } - } - prevFrame = p.frame; ++pointCount; } @@ -1233,7 +1243,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; diff -r 8b614632568c -r a964151832a7 layer/WaveformLayer.cpp --- a/layer/WaveformLayer.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/WaveformLayer.cpp Wed May 14 09:54:34 2014 +0100 @@ -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); diff -r 8b614632568c -r a964151832a7 layer/WaveformLayer.h --- a/layer/WaveformLayer.h Sat Apr 12 01:07:05 2014 -0700 +++ b/layer/WaveformLayer.h Wed May 14 09:54:34 2014 +0100 @@ -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 m_effectiveGains; diff -r 8b614632568c -r a964151832a7 layer/layer.pro --- a/layer/layer.pro Sat Apr 12 01:07:05 2014 -0700 +++ /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 diff -r 8b614632568c -r a964151832a7 svgui.pro --- a/svgui.pro Sat Apr 12 01:07:05 2014 -0700 +++ b/svgui.pro Wed May 14 09:54:34 2014 +0100 @@ -4,10 +4,25 @@ 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) { + + CONFIG += release + DEFINES += NDEBUG BUILD_RELEASE NO_TIMING + + 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 +35,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 +66,7 @@ SOURCES += layer/Colour3DPlotLayer.cpp \ layer/ColourDatabase.cpp \ layer/ColourMapper.cpp \ + layer/FlexiNoteLayer.cpp \ layer/ImageLayer.cpp \ layer/ImageRegionFinder.cpp \ layer/Layer.cpp \ diff -r 8b614632568c -r a964151832a7 view/Pane.cpp --- a/view/Pane.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/view/Pane.cpp Wed May 14 09:54:34 2014 +0100 @@ -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" @@ -85,6 +90,9 @@ updateHeadsUpDisplay(); + connect(this, SIGNAL(regionOutlined(QRect)), + this, SLOT(zoomToRegion(QRect))); + cerr << "Pane::Pane(" << this << ") returning" << endl; } @@ -167,7 +175,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); @@ -327,19 +335,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; @@ -347,25 +355,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; @@ -375,10 +383,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; } @@ -407,7 +415,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() && @@ -457,6 +465,10 @@ m_scaleWidth = 0; + if (workModel) { + drawModelTimeExtents(r, paint, workModel); + } + if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) { drawVerticalScale(r, topLayer, paint); } @@ -625,9 +637,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()); @@ -648,7 +660,7 @@ { QPoint pos = m_identifyPoint; QString desc = topLayer->getFeatureDescription(this, pos); - + if (desc != "") { paint.save(); @@ -725,7 +737,7 @@ LayerList::iterator vi = m_layers.end(); if (vi != m_layers.begin()) { - + switch ((*--vi)->getPreferredFrameCountPosition()) { case Layer::PositionTop: @@ -767,6 +779,39 @@ } void +Pane::drawModelTimeExtents(QRect r, QPainter &paint, const Model *model) +{ + int x0 = getXForFrame(model->getStartFrame()); + int x1 = getXForFrame(model->getEndFrame()); + + int lw = 10; + + paint.save(); + + QBrush brush; + + if (hasLightBackground()) { + brush = QBrush(QColor("#f8f8f8")); + paint.setPen(Qt::black); + } else { + brush = QBrush(QColor("#101010")); + paint.setPen(Qt::white); + } + + if (x0 > r.x()) { + paint.fillRect(0, 0, x0, height(), brush); + paint.drawLine(x0, 0, x0, height()); + } + + if (x1 < r.x() + r.width()) { + paint.fillRect(x1, 0, width() - x1, height(), brush); + paint.drawLine(x1, 0, x1, height()); + } + + paint.restore(); +} + +void Pane::drawAlignmentStatus(QRect r, QPainter &paint, const Model *model, bool down) { @@ -892,7 +937,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; @@ -1146,8 +1191,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); @@ -1225,47 +1270,18 @@ tr("Double-click middle button to relocate with any tool")); kr.registerShortcut(tr("Menu"), tr("Right"), tr("Show pane context menu")); - - kr.setCategory(tr("Navigate Tool Mouse Actions")); - - kr.registerShortcut(tr("Navigate"), tr("Left"), - tr("Click left button and drag to move around")); - kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"), - tr("Shift-click left button and drag to zoom to a rectangular area")); - kr.registerShortcut(tr("Relocate"), tr("Double-Click Left"), - tr("Double-click left button to jump to clicked location")); - kr.registerShortcut(tr("Edit"), tr("Double-Click Left"), - tr("Double-click left button on an item to edit it")); - - kr.setCategory(tr("Select Tool Mouse Actions")); - kr.registerShortcut(tr("Select"), tr("Left"), - tr("Click left button and drag to select region; drag region edge to resize")); - kr.registerShortcut(tr("Multi Select"), tr("Ctrl+Left"), -#ifdef Q_OS_MAC - tr("Cmd-click left button and drag to select an additional region")); -#else - tr("Ctrl-click left button and drag to select an additional region")); -#endif - kr.registerShortcut(tr("Fine Select"), tr("Shift+Left"), - tr("Shift-click left button and drag to select without snapping to items or grid")); - - kr.setCategory(tr("Edit Tool Mouse Actions")); - kr.registerShortcut(tr("Move"), tr("Left"), - tr("Click left button on an item or selected region and drag to move")); - kr.registerShortcut(tr("Edit"), tr("Double-Click Left"), - tr("Double-click left button on an item to edit it")); - - kr.setCategory(tr("Draw Tool Mouse Actions")); - kr.registerShortcut(tr("Draw"), tr("Left"), - tr("Click left button and drag to create new item")); - - kr.setCategory(tr("Measure Tool Mouse Actions")); - kr.registerShortcut(tr("Measure Area"), tr("Left"), - tr("Click left button and drag to measure a rectangular area")); - kr.registerShortcut(tr("Measure Item"), tr("Double-Click Left"), - tr("Click left button and drag to measure extents of an item or shape")); - kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"), - tr("Shift-click left button and drag to zoom to a rectangular area")); +} + +Layer * +Pane::getTopFlexiNoteLayer() +{ + for (int i = int(m_layers.size()) - 1; i >= 0; --i) { + if (LayerFactory::getInstance()->getLayerType(m_layers[i]) == + LayerFactory::FlexiNotes) { + return m_layers[i]; + } + } + return 0; } void @@ -1290,7 +1306,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; @@ -1302,12 +1318,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; @@ -1319,61 +1335,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 = getTopFlexiNoteLayer(); + if (layer) { + layer->splitStart(this, e); + } } else if (mode == ViewManager::EditMode) { @@ -1400,33 +1425,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()); - - zoomToRegion(x0, y0, x1, y1); - } + 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()); + + emit regionOutlined(QRect(x0, y0, x1 - x0, y1 - y0)); + } } else if (mode == ViewManager::SelectMode) { @@ -1435,44 +1460,72 @@ 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()) { + + //cerr << "JTEST: release with selection" << endl; + 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); + } + } + else if (m_manager && !m_manager->haveInProgressSelection()) { + + //cerr << "JTEST: release without selection" << endl; + // Get frame location of mouse + int mouseFrame = getFrameForX(e->x()); + //cerr << "JTEST: frame location of click is " << mouseFrame << endl; + // Move play head to that frame location + int playbackFrame = fmax(0,mouseFrame); + m_manager->setPlaybackFrame(playbackFrame); + } + + 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(); - } + 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 = getTopFlexiNoteLayer(); + + if (layer) { + layer->splitEnd(this, e); + update(); + + if (m_editing) { + if (!editSelectionEnd(e)) { + layer->editEnd(this, e); + update(); + } + } + } } else if (mode == ViewManager::EditMode) { - + if (m_editing) { if (!editSelectionEnd(e)) { Layer *layer = getSelectedLayer(); @@ -1481,7 +1534,7 @@ update(); } } - } + } } else if (mode == ViewManager::MeasureMode) { @@ -1524,22 +1577,32 @@ } 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) { + FlexiNoteLayer *layer = qobject_cast(getTopFlexiNoteLayer()); + if (layer) { + layer->mouseMoveEvent(this, e); //!!! ew + return; + } + } + + 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()) { @@ -1569,17 +1632,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); } @@ -1592,17 +1655,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 = getTopFlexiNoteLayer(); + if (layer) { + 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) { @@ -1685,21 +1813,26 @@ } void -Pane::zoomToRegion(int x0, int y0, int x1, int y1) +Pane::zoomToRegion(QRect r) { + int x0 = r.x(); + int y0 = r.y(); + int x1 = r.x() + r.width(); + int y1 = r.y() + r.height(); + 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); @@ -1784,7 +1917,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)) { @@ -1793,7 +1926,7 @@ newCentreFrame = 0; } -#ifdef DEBUG_PANE +#ifdef DEBUG_PANE SVDEBUG << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame << ", models end frame = " << getModelsEndFrame() << endl; #endif @@ -1912,7 +2045,7 @@ size_t resolution = 1; int snapFrameLeft = mouseFrame; int snapFrameRight = mouseFrame; - + Layer *layer = getSelectedLayer(); if (layer && !m_shiftPressed) { layer->snapToFeatureFrame(this, snapFrameLeft, @@ -1925,9 +2058,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; @@ -1993,18 +2126,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) { @@ -2028,6 +2168,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 } @@ -2062,31 +2210,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) { @@ -2106,29 +2254,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(); @@ -2306,9 +2454,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; @@ -2338,8 +2486,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; @@ -2351,25 +2499,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); @@ -2384,7 +2532,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) { @@ -2399,33 +2547,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; */ } } @@ -2509,7 +2662,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(); @@ -2561,21 +2714,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; @@ -2619,8 +2772,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)); } diff -r 8b614632568c -r a964151832a7 view/Pane.h --- a/view/Pane.h Sat Apr 12 01:07:05 2014 -0700 +++ b/view/Pane.h Wed May 14 09:54:34 2014 +0100 @@ -67,6 +67,8 @@ void rightButtonMenuRequested(QPoint position); void dropAccepted(QStringList uriList); void dropAccepted(QString text); + void doubleClickSelectInvoked(size_t frame); + void regionOutlined(QRect rect); public slots: virtual void toolModeChanged(); @@ -84,6 +86,8 @@ virtual void propertyContainerSelected(View *, PropertyContainer *pc); + void zoomToRegion(QRect r); + void mouseEnteredWidget(); void mouseLeftWidget(); @@ -103,6 +107,7 @@ void drawVerticalScale(QRect r, Layer *, QPainter &); void drawFeatureDescription(Layer *, QPainter &); void drawCentreLine(int, QPainter &, bool omitLine); + void drawModelTimeExtents(QRect, QPainter &, const Model *); void drawDurationAndRate(QRect, const Model *, int, QPainter &); void drawWorkTitle(QRect, QPainter &, const Model *); void drawLayerNames(QRect, QPainter &); @@ -129,10 +134,11 @@ void dragTopLayer(QMouseEvent *e); void dragExtendSelection(QMouseEvent *e); - void zoomToRegion(int x0, int y0, int x1, int y1); void updateContextHelp(const QPoint *pos); void edgeScrollMaybe(int x); + Layer *getTopFlexiNoteLayer(); + bool m_identifyFeatures; QPoint m_identifyPoint; QPoint m_clickPos; diff -r 8b614632568c -r a964151832a7 view/PaneStack.cpp --- a/view/PaneStack.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/view/PaneStack.cpp Wed May 14 09:54:34 2014 +0100 @@ -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(); @@ -306,8 +317,8 @@ bool multi = (getPaneCount() > 1); for (std::vector::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); } } @@ -598,24 +609,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; + } } } @@ -630,4 +661,3 @@ m_splitter->setSizes(sizes); } - diff -r 8b614632568c -r a964151832a7 view/PaneStack.h --- a/view/PaneStack.h Sat Apr 12 01:07:05 2014 -0700 +++ b/view/PaneStack.h Wed May 14 09:54:34 2014 +0100 @@ -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 m_panes; std::vector m_hiddenPanes; + bool m_showAccessories; + QSplitter *m_splitter; QStackedWidget *m_propertyStackStack; diff -r 8b614632568c -r a964151832a7 view/View.cpp --- a/view/View.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/view/View.cpp Wed May 14 09:54:34 2014 +0100 @@ -626,7 +626,15 @@ View::getSelectedLayer() { if (m_haveSelectedLayer && !m_layers.empty()) { - return getLayer(getLayerCount() - 1); + int n = getLayerCount(); + while (n > 0) { + --n; + Layer *layer = getLayer(n); + if (!(layer->isLayerDormant(this))) { + return layer; + } + } + return 0; } else { return 0; } diff -r 8b614632568c -r a964151832a7 view/View.h --- a/view/View.h Sat Apr 12 01:07:05 2014 -0700 +++ b/view/View.h Wed May 14 09:54:34 2014 +0100 @@ -200,6 +200,9 @@ virtual void drawMeasurementRect(QPainter &p, const Layer *, QRect rect, bool focus) const; + virtual bool shouldShowFeatureLabels() const { + return m_manager && m_manager->shouldShowFeatureLabels(); + } virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const { return false; } diff -r 8b614632568c -r a964151832a7 view/ViewManager.cpp --- a/view/ViewManager.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/view/ViewManager.cpp Wed May 14 09:54:34 2014 +0100 @@ -43,7 +43,7 @@ m_playSelectionMode(false), m_playSoloMode(false), m_alignMode(false), - m_overlayMode(MinimalOverlays), + m_overlayMode(StandardOverlays), m_zoomWheelsEnabled(true), m_showCentreLine(true), m_illuminateLocalFeatures(true), @@ -57,9 +57,9 @@ (settings.value("overlay-mode", int(m_overlayMode)).toInt()); if (m_overlayMode != NoOverlays && - m_overlayMode != MinimalOverlays && + m_overlayMode != StandardOverlays && m_overlayMode != AllOverlays) { - m_overlayMode = MinimalOverlays; + m_overlayMode = StandardOverlays; } m_zoomWheelsEnabled = @@ -258,6 +258,14 @@ } void +ViewManager::addSelectionQuietly(const Selection &selection) +{ + MultiSelection ms(m_selections); + ms.addSelection(selection); + setSelections(ms, true); +} + +void ViewManager::removeSelection(const Selection &selection) { MultiSelection ms(m_selections); @@ -274,11 +282,14 @@ } void -ViewManager::setSelections(const MultiSelection &ms) +ViewManager::setSelections(const MultiSelection &ms, bool quietly) { if (m_selections.getSelections() == ms.getSelections()) return; SetSelectionCommand *command = new SetSelectionCommand(this, ms); CommandHistory::getInstance()->addCommand(command); + if (!quietly) { + emit selectionChangedByUser(); + } } size_t @@ -360,9 +371,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) { @@ -646,11 +680,13 @@ m_lightPalette = QApplication::palette(); } +#ifndef Q_OS_MAC if (dark) { QApplication::setPalette(m_darkPalette); } else { QApplication::setPalette(m_lightPalette); } +#endif } bool diff -r 8b614632568c -r a964151832a7 view/ViewManager.h --- a/view/ViewManager.h Sat Apr 12 01:07:05 2014 -0700 +++ b/view/ViewManager.h Wed May 14 09:54:34 2014 +0100 @@ -86,6 +86,13 @@ size_t constrainFrameToSelection(size_t frame) const; /** + * Adding a selection normally emits the selectionChangedByUser + * signal. Call this to add a selection without emitting that signal. + * This is used in session file load, for example. + */ + void addSelectionQuietly(const Selection &selection); + + /** * Return the selection that contains a given frame. * If defaultToFollowing is true, and if the frame is not in a * selected area, return the next selection after the given frame. @@ -101,11 +108,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); @@ -147,7 +162,8 @@ enum OverlayMode { NoOverlays, - MinimalOverlays, + GlobalOverlays, + StandardOverlays, AllOverlays }; void setOverlayMode(OverlayMode mode); @@ -169,7 +185,7 @@ return m_overlayMode == AllOverlays; } bool shouldShowSelectionExtents() const { - return m_overlayMode != NoOverlays; + return m_overlayMode != NoOverlays && m_overlayMode != GlobalOverlays; } bool shouldShowLayerNames() const { return m_overlayMode == AllOverlays; @@ -183,6 +199,9 @@ bool shouldIlluminateLocalFeatures() const { return m_illuminateLocalFeatures; } + bool shouldShowFeatureLabels() const { + return m_overlayMode != NoOverlays && m_overlayMode != GlobalOverlays; + } void setZoomWheelsEnabled(bool enable); bool getZoomWheelsEnabled() const { return m_zoomWheelsEnabled; } @@ -206,9 +225,14 @@ /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */ void outputLevelsChanged(float left, float right); - /** Emitted when the selection has changed. */ + /** Emitted whenever the selection has changed. */ void selectionChanged(); + /** Emitted when the selection has been changed through an + * explicit selection-editing action. *Not* emitted when the + * selection has been changed through undo or redo. */ + void selectionChangedByUser(); + /** Emitted when the in-progress (rubberbanding) selection has changed. */ void inProgressSelectionChanged(); @@ -273,13 +297,14 @@ Clipboard m_clipboard; ToolMode m_toolMode; + std::map m_toolModeOverrides; bool m_playLoopMode; bool m_playSelectionMode; bool m_playSoloMode; bool m_alignMode; - void setSelections(const MultiSelection &ms); + void setSelections(const MultiSelection &ms, bool quietly = false); void signalSelectionChange(); class SetSelectionCommand : public Command diff -r 8b614632568c -r a964151832a7 view/view.pro --- a/view/view.pro Sat Apr 12 01:07:05 2014 -0700 +++ /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 diff -r 8b614632568c -r a964151832a7 widgets/ActivityLog.cpp --- a/widgets/ActivityLog.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/ActivityLog.cpp Wed May 14 09:54:34 2014 +0100 @@ -25,6 +25,13 @@ #include +#include "base/Debug.h" + +using std::cerr; +using std::endl; + +#define PRINT_ACTIVITY 1 + ActivityLog::ActivityLog() : QDialog() { setWindowTitle(tr("Activity Log")); @@ -53,9 +60,16 @@ ActivityLog::activityHappened(QString name) { name = name.replace("&", ""); -// SVDEBUG << "ActivityLog::activityHappened(" << name << ")" << endl; + +#ifdef PRINT_ACTIVITY + cerr << "ActivityLog: " << name; if (name == m_prevName) { -// cerr << "(ignoring duplicate)" << endl; + cerr << " (duplicate)"; + } + cerr << endl; +#endif + + if (name == m_prevName) { return; } m_prevName = name; diff -r 8b614632568c -r a964151832a7 widgets/AudioDial.cpp --- a/widgets/AudioDial.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/AudioDial.cpp Wed May 14 09:54:34 2014 +0100 @@ -231,31 +231,31 @@ c = c.light(110); } - // Scale shadow... + // Scale shadow, omitting the bottom part... shadowAngle = 2160; - c = palette().dark().color(); - for (int arc = 120; arc < 2880; arc += 240) { + c = palette().shadow().color(); + for (int i = 0; i < 5; ++i) { pen.setColor(c); paint.setPen(pen); + int arc = i * 240 + 120; paint.drawArc(scale/2, scale/2, width-scale, width-scale, shadowAngle + arc, 240); + c = c.light(110); + } + c = palette().shadow().color(); + for (int i = 0; i < 12; ++i) { + pen.setColor(c); + paint.setPen(pen); + int arc = i * 240 + 120; paint.drawArc(scale/2, scale/2, width-scale, width-scale, shadowAngle - arc, 240); - c = c.light(108); + c = c.light(110); } - // Undraw the bottom part... - - pen.setColor(palette().background().color()); - pen.setWidth(scale * 4); - paint.setPen(pen); - paint.drawArc(scale/2, scale/2, - width-scale, width-scale, -45 * 16, -92 * 16); - // Scale ends... - pen.setColor(palette().dark().color()); + pen.setColor(palette().shadow().color()); pen.setWidth(scale); paint.setPen(pen); for (int i = 0; i < numTicks; ++i) { diff -r 8b614632568c -r a964151832a7 widgets/CommandHistory.cpp --- a/widgets/CommandHistory.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/CommandHistory.cpp Wed May 14 09:54:34 2014 +0100 @@ -37,7 +37,7 @@ #include -//#define DEBUG_COMMAND_HISTORY 1 +#define DEBUG_COMMAND_HISTORY 1 CommandHistory *CommandHistory::m_instance = 0; @@ -101,7 +101,7 @@ CommandHistory::clear() { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::clear()" << endl; + cerr << "CommandHistory::clear()" << endl; #endif closeBundle(); m_savedAt = -1; @@ -143,7 +143,7 @@ if (!command) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addCommand: " << command->getName() << " of type " << typeid(*command).name() << " at " << command << ": execute = " << execute << ", bundle = " << bundle << " (m_currentCompound = " << m_currentCompound << ", m_currentBundle = " << m_currentBundle << ")" << endl; + cerr << "CommandHistory::addCommand: " << command->getName() << " of type " << typeid(*command).name() << " at " << command << ": execute = " << execute << ", bundle = " << bundle << " (m_currentCompound = " << m_currentCompound << ", m_currentBundle = " << m_currentBundle << ")" << endl; #endif if (m_currentCompound) { @@ -160,7 +160,7 @@ #ifdef DEBUG_COMMAND_HISTORY if (!m_redoStack.empty()) { - SVDEBUG << "CommandHistory::clearing redo stack" << endl; + cerr << "CommandHistory::clearing redo stack" << endl; } #endif @@ -192,8 +192,8 @@ if (m_currentBundle) { if (!command || (command->getName() != m_currentBundleName)) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": closing current bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": closing current bundle" << endl; #endif closeBundle(); } @@ -204,8 +204,8 @@ if (!m_currentBundle) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": creating new bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": creating new bundle" << endl; #endif // need to addCommand before setting m_currentBundle, as addCommand @@ -219,8 +219,8 @@ } #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": adding to bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": adding to bundle" << endl; #endif if (execute) command->execute(); @@ -242,11 +242,12 @@ void CommandHistory::closeBundle() { + if (m_currentBundle) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::closeBundle" << endl; + cerr << "CommandHistory::closeBundle" << endl; #endif - - if (m_currentBundle) emit activity(m_currentBundle->getName()); + emit activity(m_currentBundle->getName()); + } m_currentBundle = 0; m_currentBundleName = ""; } @@ -255,7 +256,7 @@ CommandHistory::bundleTimerTimeout() { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::bundleTimerTimeout: bundle is " << m_currentBundle << endl; + cerr << "CommandHistory::bundleTimerTimeout: bundle is " << m_currentBundle << endl; #endif closeBundle(); @@ -264,14 +265,15 @@ void CommandHistory::addToCompound(Command *command, bool execute) { -#ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToCompound: " << command->getName() << endl; -#endif if (!m_currentCompound) { - SVDEBUG << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; return; } +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::addToCompound[" << m_currentCompound->getName() << "]: " << command->getName() << " (exec: " << execute << ")" << endl; +#endif + if (execute) command->execute(); m_currentCompound->addCommand(command); } @@ -280,13 +282,17 @@ CommandHistory::startCompoundOperation(QString name, bool execute) { if (m_currentCompound) { - SVDEBUG << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; + cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; cerr << "(name is " << m_currentCompound->getName() << ")" << endl; return; } +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::startCompoundOperation: " << name << " (exec: " << execute << ")" << endl; +#endif + closeBundle(); - + m_currentCompound = new MacroCommand(name); m_executeCompound = execute; } @@ -295,9 +301,13 @@ CommandHistory::endCompoundOperation() { if (!m_currentCompound) { - SVDEBUG << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; return; } + +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::endCompoundOperation: " << m_currentCompound->getName() << endl; +#endif MacroCommand *toAdd = m_currentCompound; m_currentCompound = 0; @@ -329,7 +339,7 @@ if (m_undoStack.empty()) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::undo()" << endl; + cerr << "CommandHistory::undo()" << endl; #endif closeBundle(); @@ -355,7 +365,7 @@ if (m_redoStack.empty()) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::redo()" << endl; + cerr << "CommandHistory::redo()" << endl; #endif closeBundle(); @@ -436,7 +446,7 @@ for (i = 0; i < limit; ++i) { #ifdef DEBUG_COMMAND_HISTORY Command *command = stack.top(); - SVDEBUG << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; + cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; #endif tempStack.push(stack.top()); stack.pop(); @@ -458,7 +468,7 @@ Command *command = stack.top(); // Not safe to call getName() on a command about to be deleted #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::clearStack: About to delete command " << command << endl; + cerr << "CommandHistory::clearStack: About to delete command " << command << endl; #endif delete command; stack.pop(); diff -r 8b614632568c -r a964151832a7 widgets/InteractiveFileFinder.cpp --- a/widgets/InteractiveFileFinder.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/InteractiveFileFinder.cpp Wed May 14 09:54:34 2014 +0100 @@ -33,6 +33,7 @@ InteractiveFileFinder::m_instance; InteractiveFileFinder::InteractiveFileFinder() : + m_sessionExtension("sv"), m_lastLocatedLocation("") { SVDEBUG << "Registering interactive file finder" << endl; @@ -43,6 +44,12 @@ { } +void +InteractiveFileFinder::setApplicationSessionExtension(QString extension) +{ + m_sessionExtension = extension; +} + QString InteractiveFileFinder::getOpenFileName(FileType type, QString fallbackLocation) { @@ -57,7 +64,10 @@ case SessionFile: settingsKey = "sessionpath"; title = tr("Select a session file"); - filter = tr("Sonic Visualiser session files (*.sv)\nRDF files (%1)\nAll files (*.*)").arg(RDFImporter::getKnownExtensions()); + filter = tr("%1 session files (*.%1)\nRDF files (%3)\nAll files (*.*)") + .arg(QApplication::applicationName()) + .arg(m_sessionExtension) + .arg(RDFImporter::getKnownExtensions()); break; case AudioFile: @@ -81,11 +91,27 @@ .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 (*.*)") + filter = tr("All supported files (*.sv %1 %2)\n%3 session files (*.%4)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)") .arg(RDFImporter::getKnownExtensions()) - .arg(AudioFileReaderFactory::getKnownExtensions()); + .arg(AudioFileReaderFactory::getKnownExtensions()) + .arg(QApplication::applicationName()) + .arg(m_sessionExtension); break; case ImageFile: @@ -109,10 +135,12 @@ case AnyFile: settingsKey = "lastpath"; - filter = tr("All supported files (*.sv %1 %2 %3)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") + filter = tr("All supported files (*.sv %1 %2 %3)\n%4 session files (*.%5)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") .arg(AudioFileReaderFactory::getKnownExtensions()) .arg(DataFileReaderFactory::getKnownExtensions()) - .arg(RDFImporter::getKnownExtensions()); + .arg(RDFImporter::getKnownExtensions()) + .arg(QApplication::applicationName()) + .arg(m_sessionExtension); break; }; @@ -202,7 +230,8 @@ case SessionFile: settingsKey = "savesessionpath"; title = tr("Select a session file"); - filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); + filter = tr("%1 session files (*.%2)\nAll files (*.*)") + .arg(QApplication::applicationName()).arg(m_sessionExtension); break; case AudioFile: @@ -224,6 +253,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(); @@ -274,7 +315,7 @@ dialog.setConfirmOverwrite(false); // we'll do that if (type == SessionFile) { - dialog.setDefaultSuffix("sv"); + dialog.setDefaultSuffix(m_sessionExtension); } else if (type == AudioFile) { dialog.setDefaultSuffix("wav"); } else if (type == ImageFile) { @@ -299,7 +340,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 +411,14 @@ settingsKey = "layerpath"; break; + case LayerFileNonSV: + settingsKey = "layerpath"; + break; + + case LayerFileNoMidiNonSV: + settingsKey = "layerpath"; + break; + case SessionOrAudioFile: settingsKey = "lastpath"; break; diff -r 8b614632568c -r a964151832a7 widgets/InteractiveFileFinder.h --- a/widgets/InteractiveFileFinder.h Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/InteractiveFileFinder.h Wed May 14 09:54:34 2014 +0100 @@ -18,6 +18,7 @@ #include "data/fileio/FileFinder.h" +#include #include #include @@ -29,6 +30,14 @@ public: virtual ~InteractiveFileFinder(); + /// Specify the extension for this application's session files + /// (without the dot) + void setApplicationSessionExtension(QString extension); + + QString getApplicationSessionExtension() const { + return m_sessionExtension; + } + QString getOpenFileName(FileType type, QString fallbackLocation = ""); QString getSaveFileName(FileType type, QString fallbackLocation = ""); void registerLastOpenedFilePath(FileType type, QString path); @@ -44,6 +53,7 @@ QString findRelative(QString location, QString relativeTo); QString locateInteractive(FileType type, QString thing); + QString m_sessionExtension; QString m_lastLocatedLocation; }; diff -r 8b614632568c -r a964151832a7 widgets/KeyReference.cpp --- a/widgets/KeyReference.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/KeyReference.cpp Wed May 14 09:54:34 2014 +0100 @@ -49,7 +49,7 @@ QString name = action->text(); if (overrideName != "") name = overrideName; - QString shortcut = action->shortcut().toString(); + QString shortcut = action->shortcut().toString(QKeySequence::NativeText); QString tip = action->statusTip(); registerShortcut(name, shortcut, tip); @@ -87,6 +87,13 @@ } void +KeyReference::registerAlternativeShortcut(QAction *action, QKeySequence shortcut) +{ + QString name = action->text(); + registerAlternativeShortcut(name, shortcut.toString(QKeySequence::NativeText)); +} + +void KeyReference::registerAlternativeShortcut(QString name, QString alternative) { name.replace(tr("&"), ""); @@ -102,6 +109,12 @@ } void +KeyReference::registerAlternativeShortcut(QString name, QKeySequence shortcut) +{ + registerAlternativeShortcut(name, shortcut.toString(QKeySequence::NativeText)); +} + +void KeyReference::show() { if (m_dialog) { @@ -147,7 +160,7 @@ altdesc = tr(" (%1)").arg(altdesc); } - text += QString(" %1%2 %3%4\n") + text += QString(" %1%2 %3%4\n") .arg(shortcut).arg(altdesc).arg(actionName).arg(tip); } } @@ -159,7 +172,8 @@ m_text->setReadOnly(true); m_dialog = new QDialog; - m_dialog->setWindowTitle(tr("Sonic Visualiser: Key and Mouse Reference")); + m_dialog->setWindowTitle(tr("%1: Key and Mouse Reference") + .arg(QApplication::applicationName())); QVBoxLayout *layout = new QVBoxLayout; m_dialog->setLayout(layout); diff -r 8b614632568c -r a964151832a7 widgets/KeyReference.h --- a/widgets/KeyReference.h Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/KeyReference.h Wed May 14 09:54:34 2014 +0100 @@ -20,6 +20,7 @@ #include #include #include +#include class QAction; class QTextEdit; @@ -38,9 +39,11 @@ void registerShortcut(QAction *, QString overrideName = ""); void registerAlternativeShortcut(QAction *, QString alternative); + void registerAlternativeShortcut(QAction *, QKeySequence alternative); void registerShortcut(QString actionName, QString shortcut, QString tipText); void registerAlternativeShortcut(QString actionName, QString alternative); + void registerAlternativeShortcut(QString actionName, QKeySequence alternative); void show(); void hide(); diff -r 8b614632568c -r a964151832a7 widgets/PropertyBox.cpp --- a/widgets/PropertyBox.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/PropertyBox.cpp Wed May 14 09:54:34 2014 +0100 @@ -24,10 +24,6 @@ #include "base/UnitDatabase.h" #include "base/RangeMapper.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - #include "AudioDial.h" #include "LEDButton.h" #include "IconLoader.h" @@ -46,6 +42,7 @@ #include #include #include +#include #include #include @@ -188,13 +185,14 @@ layout->insertStretch(-1, 10); - if (params->getPlayPluginId() != "") { - QPushButton *pluginButton = new QPushButton(QIcon(":icons/faders.png"), ""); - pluginButton->setFixedWidth(24); - pluginButton->setFixedHeight(24); - layout->addWidget(pluginButton); - connect(pluginButton, SIGNAL(clicked()), - this, SLOT(editPlugin())); + if (params->getPlayClipId() != "") { + QPushButton *playParamButton = + new QPushButton(QIcon(":icons/faders.png"), ""); + playParamButton->setFixedWidth(24); + playParamButton->setFixedHeight(24); + layout->addWidget(playParamButton); + connect(playParamButton, SIGNAL(clicked()), + this, SLOT(editPlayParameters())); } AudioDial *gainDial = new AudioDial; @@ -461,7 +459,7 @@ // manages its own Add New Colour entry... ColourDatabase *db = ColourDatabase::getInstance(); - for (size_t i = 0; i < db->getColourCount(); ++i) { + for (int i = 0; i < db->getColourCount(); ++i) { QString name = db->getColourName(i); cb->addItem(db->getExamplePixmap(i, QSize(12, 12)), name); } @@ -746,55 +744,56 @@ } void -PropertyBox::editPlugin() +PropertyBox::editPlayParameters() { - //!!! should probably just emit and let something else do this - PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - QString pluginId = params->getPlayPluginId(); - QString configurationXml = params->getPlayPluginConfiguration(); + QString clip = params->getPlayClipId(); PlayParameterRepository::EditCommand *command = new PlayParameterRepository::EditCommand(params); - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - if (!factory) return; + QInputDialog *dialog = new QInputDialog(this); - RealTimePluginInstance *instance = - factory->instantiatePlugin(pluginId, 0, 0, 48000, 1024, 1); - if (!instance) return; + QDir dir(":/samples"); + QStringList clipFiles = dir.entryList(QStringList() << "*.wav", QDir::Files); - PluginXml(instance).setParametersFromXml(configurationXml); + QStringList clips; + foreach (QString str, clipFiles) { + clips.push_back(str.replace(".wav", "")); + } + dialog->setComboBoxItems(clips); - PluginParameterDialog *dialog = new PluginParameterDialog(instance); - connect(dialog, SIGNAL(pluginConfigurationChanged(QString)), - this, SLOT(pluginConfigurationChanged(QString))); + dialog->setLabelText(tr("Set playback clip:")); + + QComboBox *cb = dialog->findChild(); + if (cb) cb->setCurrentText(clip); + + connect(dialog, SIGNAL(textValueChanged(QString)), + this, SLOT(playClipChanged(QString))); if (dialog->exec() == QDialog::Accepted) { - QString newConfiguration = PluginXml(instance).toXmlString(); - command->setPlayPluginConfiguration(newConfiguration); + QString newClip = dialog->textValue(); + command->setPlayClipId(newClip); CommandHistory::getInstance()->addCommand(command, true); } else { delete command; // restore in case we mucked about with the configuration // as a consequence of signals from the dialog - params->setPlayPluginConfiguration(configurationXml); + params->setPlayClipId(clip); } delete dialog; - delete instance; } void -PropertyBox::pluginConfigurationChanged(QString configurationXml) +PropertyBox::playClipChanged(QString id) { PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - params->setPlayPluginConfiguration(configurationXml); + params->setPlayClipId(id); } void diff -r 8b614632568c -r a964151832a7 widgets/PropertyBox.h --- a/widgets/PropertyBox.h Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/PropertyBox.h Wed May 14 09:54:34 2014 +0100 @@ -47,7 +47,7 @@ public slots: void propertyContainerPropertyChanged(PropertyContainer *); void propertyContainerPropertyRangeChanged(PropertyContainer *); - void pluginConfigurationChanged(QString); + void playClipChanged(QString); void layerVisibilityChanged(bool); protected slots: @@ -66,7 +66,7 @@ void unitDatabaseChanged(); void colourDatabaseChanged(); - void editPlugin(); + void editPlayParameters(); void mouseEnteredWidget(); void mouseLeftWidget(); diff -r 8b614632568c -r a964151832a7 widgets/PropertyStack.cpp --- a/widgets/PropertyStack.cpp Sat Apr 12 01:07:05 2014 -0700 +++ b/widgets/PropertyStack.cpp Wed May 14 09:54:34 2014 +0100 @@ -23,6 +23,7 @@ #include "widgets/IconLoader.h" #include "base/Command.h" #include "widgets/CommandHistory.h" +#include "layer/ShowLayerCommand.h" #include #include @@ -157,9 +158,19 @@ int PropertyStack::getContainerIndex(PropertyContainer *pc) const { - for (size_t i = 0; i < m_client->getPropertyContainerCount(); ++i) { - PropertyContainer *container = m_client->getPropertyContainer(i); - if (pc == container) return i; + // This is used to obtain an index to be passed to setCurrentIndex + // -- which is the index of the property container's box in our + // stack of boxes. That is not the same thing as the index of the + // container (i.e. the layer) in the view: the view reorders its + // containers whenever one is raised to the top, while our boxes + // remain in the same order. So we must find this container in the + // box list, not in the view. + + for (size_t i = 0; i < m_boxes.size(); ++i) { + PropertyContainer *container = m_boxes[i]->getContainer(); + if (pc == container) { + return i; + } } return false; @@ -211,27 +222,6 @@ repopulate(); } -class ShowLayerCommand : public QObject, public Command -{ -public: - ShowLayerCommand(View *view, Layer *layer, bool show, QString name) : - m_view(view), m_layer(layer), m_show(show), m_name(name) { } - void execute() { - m_layer->showLayer(m_view, m_show); - } - void unexecute() { - m_layer->showLayer(m_view, !m_show); - } - QString getName() const { - return m_name; - } -protected: - View *m_view; - Layer *m_layer; - bool m_show; - QString m_name; -}; - void PropertyStack::showLayer(bool show) { diff -r 8b614632568c -r a964151832a7 widgets/widgets.pro --- a/widgets/widgets.pro Sat Apr 12 01:07:05 2014 -0700 +++ /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