# HG changeset patch # User Chris Cannam # Date 1558083772 -3600 # Node ID 42c87368287c0c0e7882e71472443f99f7fa9da9 # Parent 8d5bf4ab98ef22d646cf093eea47b439218eb6a2# Parent 009f22e03bf6b4cb6eade4a11b2b6eb0acb9e6f3 Merge from branch single-point diff -r 8d5bf4ab98ef -r 42c87368287c layer/Colour3DPlotLayer.cpp --- a/layer/Colour3DPlotLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/Colour3DPlotLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -147,9 +147,10 @@ connectSignals(m_model); - connect(m_model, SIGNAL(modelChanged()), this, SLOT(modelChanged())); + connect(m_model, SIGNAL(modelChanged()), + this, SLOT(handleModelChanged())); connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); + this, SLOT(handleModelChangedWithin(sv_frame_t, sv_frame_t))); m_peakResolution = 256; if (model->getResolution() > 512) { @@ -160,35 +161,22 @@ m_peakResolution = 128; } - if (m_peakCache) m_peakCache->aboutToDelete(); - delete m_peakCache; - m_peakCache = nullptr; - - invalidateRenderers(); - invalidateMagnitudes(); + invalidatePeakCache(); emit modelReplaced(); emit sliceableModelReplaced(oldModel, model); } void -Colour3DPlotLayer::cacheInvalid() +Colour3DPlotLayer::invalidatePeakCache() { + // renderers use the peak cache, so we must invalidate those too invalidateRenderers(); invalidateMagnitudes(); -} - -void -Colour3DPlotLayer::cacheInvalid(sv_frame_t /* startFrame */, - sv_frame_t /* endFrame */) -{ - //!!! should do this only if the range is visible + if (m_peakCache) m_peakCache->aboutToDelete(); delete m_peakCache; m_peakCache = nullptr; - - invalidateRenderers(); - invalidateMagnitudes(); } void @@ -220,7 +208,7 @@ } void -Colour3DPlotLayer::modelChanged() +Colour3DPlotLayer::handleModelChanged() { if (!m_colourScaleSet && m_colourScale == ColourScaleType::Linear) { if (m_model) { @@ -231,11 +219,12 @@ } } } - cacheInvalid(); + invalidatePeakCache(); + emit modelChanged(); } void -Colour3DPlotLayer::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) +Colour3DPlotLayer::handleModelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) { if (!m_colourScaleSet && m_colourScale == ColourScaleType::Linear) { if (m_model && m_model->getWidth() > 50) { @@ -246,7 +235,7 @@ } } } - cacheInvalid(startFrame, endFrame); + emit modelChangedWithin(startFrame, endFrame); } Layer::PropertyList @@ -620,6 +609,13 @@ return m_smooth; } +bool +Colour3DPlotLayer::hasLightBackground() const +{ + return ColourMapper(m_colourMap, m_colourInverted, 1.f, 255.f) + .hasLightBackground(); +} + void Colour3DPlotLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) { @@ -636,7 +632,7 @@ Layer::setLayerDormant(v, true); - cacheInvalid(); + invalidatePeakCache(); // for memory-saving purposes } else { @@ -1189,7 +1185,6 @@ switch (snap) { case SnapLeft: frame = left; break; case SnapRight: frame = right; break; - case SnapNearest: case SnapNeighbouring: if (frame - left > right - frame) frame = right; else frame = left; diff -r 8d5bf4ab98ef -r 42c87368287c layer/Colour3DPlotLayer.h --- a/layer/Colour3DPlotLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/Colour3DPlotLayer.h Fri May 17 10:02:52 2019 +0100 @@ -129,6 +129,8 @@ void setSmooth(bool i); bool getSmooth() const; + bool hasLightBackground() const override; + bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const override; @@ -149,10 +151,8 @@ QString extraAttributes = "") const override; protected slots: - void cacheInvalid(); - void cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame); - void modelChanged(); - void modelChangedWithin(sv_frame_t, sv_frame_t); + void handleModelChanged(); + void handleModelChangedWithin(sv_frame_t, sv_frame_t); protected: const DenseThreeDimensionalModel *m_model; // I do not own this @@ -185,6 +185,7 @@ mutable Dense3DModelPeakCache *m_peakCache; const int m_peakCacheDivisor; Dense3DModelPeakCache *getPeakCache() const; + void invalidatePeakCache(); typedef std::map ViewMagMap; // key is view id mutable ViewMagMap m_viewMags; diff -r 8d5bf4ab98ef -r 42c87368287c layer/Colour3DPlotRenderer.cpp --- a/layer/Colour3DPlotRenderer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/Colour3DPlotRenderer.cpp Fri May 17 10:02:52 2019 +0100 @@ -113,7 +113,7 @@ << predicted << " (" << m_secondsPerXPixel << " x " << rect.width() << ")" << endl; #endif - if (predicted < 0.2) { + if (predicted < 0.175) { #ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "Predicted time looks fast enough: no partial renders" << endl; @@ -129,6 +129,10 @@ if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth(); sv_frame_t startFrame = v->getStartFrame(); + + bool justInvalidated = + (m_cache.getSize() != v->getPaintSize() || + m_cache.getZoomLevel() != v->getZoomLevel()); m_cache.resize(v->getPaintSize()); m_cache.setZoomLevel(v->getZoomLevel()); @@ -153,7 +157,7 @@ #endif static HitCount count("Colour3DPlotRenderer: image cache"); - + if (m_cache.isValid()) { // some part of the cache is valid if (v->getXForFrame(m_cache.getStartFrame()) == @@ -292,7 +296,11 @@ } else { // must be DrawBufferPixelResolution, handled DirectTranslucent earlier - renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained); + if (timeConstrained && justInvalidated) { + SVDEBUG << "render: just invalidated cache in time-constrained context, that's all we're doing for now - wait for next update to start filling" << endl; + } else { + renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained); + } } QRect pr = rect & m_cache.getValidArea(); @@ -301,7 +309,13 @@ if (!timeConstrained && (pr != rect)) { SVCERR << "WARNING: failed to render entire requested rect " - << "even when not time-constrained" << endl; + << "even when not time-constrained: wanted " + << rect.x() << "," << rect.y() << " " + << rect.width() << "x" << rect.height() << ", got " + << pr.x() << "," << pr.y() << " " + << pr.width() << "x" << pr.height() + << ", after request of width " << (x1 - x0) + << endl; } MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0); @@ -599,6 +613,11 @@ for (int ix = 0; in_range_for(m_sources.peakCaches, ix); ++ix) { int bpp = m_sources.peakCaches[ix]->getColumnsPerPeak(); ZoomLevel equivZoom(ZoomLevel::FramesPerPixel, binResolution * bpp); +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "getPreferredPeakCache: zoomLevel = " << zoomLevel + << ", cache " << ix << " has bpp = " << bpp + << " for equivZoom = " << equivZoom << endl; +#endif if (zoomLevel >= equivZoom) { // this peak cache would work, though it might not be best if (bpp > binsPerPeak) { @@ -612,9 +631,9 @@ #ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "getPreferredPeakCache: zoomLevel = " << zoomLevel << ", binResolution " << binResolution - << ", binsPerPeak " << binsPerPeak - << ", peakCacheIndex " << peakCacheIndex << ", peakCaches " << m_sources.peakCaches.size() + << ": preferring peakCacheIndex " << peakCacheIndex + << " for binsPerPeak " << binsPerPeak << endl; #endif } @@ -983,6 +1002,7 @@ #ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "modelWidth " << modelWidth << ", divisor " << divisor << endl; + SVDEBUG << "start = " << start << ", finish = " << finish << ", step = " << step << endl; #endif for (int x = start; x != finish; x += step) { @@ -1074,7 +1094,7 @@ double fractionComplete = double(xPixelCount) / double(w); if (timer.outOfTime(fractionComplete)) { #ifdef DEBUG_COLOUR_PLOT_REPAINT - SVDEBUG << "out of time" << endl; + SVDEBUG << "out of time with xPixelCount = " << xPixelCount << endl; #endif updateTimings(timer, xPixelCount); return xPixelCount; @@ -1082,6 +1102,10 @@ } updateTimings(timer, xPixelCount); + +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "completed with xPixelCount = " << xPixelCount << endl; +#endif return xPixelCount; } @@ -1263,7 +1287,8 @@ #ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "across " << xPixelCount << " x-pixels, seconds per x-pixel = " - << m_secondsPerXPixel << endl; + << m_secondsPerXPixel << " (total = " + << (xPixelCount * m_secondsPerXPixel) << endl; #endif } } diff -r 8d5bf4ab98ef -r 42c87368287c layer/ColourDatabase.cpp --- a/layer/ColourDatabase.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/ColourDatabase.cpp Fri May 17 10:02:52 2019 +0100 @@ -18,6 +18,8 @@ #include +//#define DEBUG_COLOUR_DATABASE 1 + ColourDatabase ColourDatabase::m_instance; @@ -85,24 +87,70 @@ return -1; } +int +ColourDatabase::getNearbyColourIndex(QColor col) const +{ + int index = 0; + int closestIndex = -1; + int closestDistance = 0; + + for (auto &c: m_colours) { + int distance = + std::abs(col.red() - c.colour.red()) + + std::abs(col.green() - c.colour.green()) + + std::abs(col.blue() - c.colour.blue()); +#ifdef DEBUG_COLOUR_DATABASE + SVDEBUG << "getNearbyColourIndex: comparing " << c.colour.name() + << " to " << col.name() << ": distance = " << distance << endl; +#endif + if (closestIndex < 0 || distance < closestDistance) { + closestIndex = index; + closestDistance = distance; +#ifdef DEBUG_COLOUR_DATABASE + SVDEBUG << "(this is the best so far)" << endl; +#endif + } + ++index; + } + +#ifdef DEBUG_COLOUR_DATABASE + SVDEBUG << "returning " << closestIndex << endl; +#endif + return closestIndex; +} + QColor ColourDatabase::getContrastingColour(int c) const { QColor col = getColour(c); - if (col.red() > col.blue()) { - if (col.green() > col.blue()) { - return Qt::blue; + QColor contrasting = Qt::red; + bool dark = (col.red() < 240 && col.green() < 240 && col.blue() < 240); + if (dark) { + if (col.red() > col.blue()) { + if (col.green() > col.blue()) { + contrasting = Qt::blue; + } else { + contrasting = Qt::yellow; + } } else { - return Qt::yellow; + if (col.green() > col.blue()) { + contrasting = Qt::yellow; + } else { + contrasting = Qt::red; + } } } else { - if (col.green() > col.blue()) { - return Qt::yellow; + if (col.red() > 230 && col.green() > 230 && col.blue() > 230) { + contrasting = QColor(30, 150, 255); } else { - return Qt::red; + contrasting = QColor(255, 188, 80); } } - return Qt::red; +#ifdef DEBUG_COLOUR_DATABASE + SVDEBUG << "getContrastingColour(" << col.name() << "): dark = " << dark + << ", returning " << contrasting.name() << endl; +#endif + return contrasting; } bool diff -r 8d5bf4ab98ef -r 42c87368287c layer/ColourDatabase.h --- a/layer/ColourDatabase.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/ColourDatabase.h Fri May 17 10:02:52 2019 +0100 @@ -30,21 +30,90 @@ public: static ColourDatabase *getInstance(); + /** + * Return the number of colours in the database. + */ int getColourCount() const; + + /** + * Return the name of the colour at index c. + */ QString getColourName(int c) const; + + /** + * Return the colour at index c. + */ QColor getColour(int c) const; + + /** + * Return the colour with the given name, if found in the + * database. If not found, return Qt::black. + */ QColor getColour(QString name) const; - int getColourIndex(QString name) const; // -1 -> not found - int getColourIndex(QColor c) const; // returns first index of possibly many + + /** + * Return the index of the colour with the given name, if found in + * the database. If not found, return -1. + */ + int getColourIndex(QString name) const; + + /** + * Return the index of the given colour, if found in the + * database. If not found, return -1. Note that it is possible for + * a colour to appear more than once in the database: names have + * to be unique in the database, but colours don't. This always + * returns the first match. + */ + int getColourIndex(QColor c) const; + + /** + * Return true if the given colour exists in the database. + */ bool haveColour(QColor c) const; + /** + * Return the index of the colour in the database that is closest + * to the given one, by some simplistic measure (Manhattan + * distance in RGB space). This always returns some valid index, + * unless the database is empty, in which case it returns -1. + */ + int getNearbyColourIndex(QColor c) const; + + /** + * Add a colour to the database, with the associated name. Return + * the index of the colour in the database. Names are unique + * within the database: if another colour exists already with the + * given name, its colour value is replaced with the given + * one. Colours may appear more than once under different names. + */ + int addColour(QColor c, QString name); + + /** + * Remove the colour with the given name from the database. + */ + void removeColour(QString); + + /** + * Return true if the colour at index c is marked as using a dark + * background. Such colours are presumably "bright" ones, but all + * this reports is whether the colour has been marked with + * setUseDarkBackground, not any intrinsic property of the colour. + */ bool useDarkBackground(int c) const; + + /** + * Mark the colour at index c as using a dark + * background. Generally this should be called for "bright" + * colours. + */ void setUseDarkBackground(int c, bool dark); - int addColour(QColor, QString); // returns index - void removeColour(QString); - - // returned colour is not necessarily in database + /** + * Return a colour that contrasts with the one at index c, + * according to some simplistic algorithm. The returned colour is + * not necessarily in the database; pass it to + * getNearbyColourIndex if you need one that is. + */ QColor getContrastingColour(int c) const; // for use in XML export @@ -61,7 +130,10 @@ // for use by PropertyContainer getPropertyRangeAndValue methods void getColourPropertyRange(int *min, int *max) const; - QPixmap getExamplePixmap(int index, QSize size) const; + /** + * Generate a swatch pixmap illustrating the colour at index c. + */ + QPixmap getExamplePixmap(int c, QSize size) const; signals: void colourDatabaseChanged(); diff -r 8d5bf4ab98ef -r 42c87368287c layer/FlexiNoteLayer.cpp --- a/layer/FlexiNoteLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/FlexiNoteLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -30,7 +30,7 @@ #include "LogNumericalScale.h" #include "PaintAssistant.h" -#include "data/model/FlexiNoteModel.h" +#include "data/model/NoteModel.h" #include "view/View.h" @@ -49,19 +49,10 @@ #include // GF: included to compile std::numerical_limits on linux #include +#define NOTE_HEIGHT 16 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(nullptr), m_editing(false), m_intelligentActions(true), @@ -82,7 +73,7 @@ } void -FlexiNoteLayer::setModel(FlexiNoteModel *model) +FlexiNoteLayer::setModel(NoteModel *model) { if (m_model == model) return; m_model = model; @@ -407,71 +398,46 @@ return mapper; } -FlexiNoteModel::PointList +EventVector FlexiNoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { - if (!m_model) return FlexiNoteModel::PointList(); - + if (!m_model) return {}; + sv_frame_t frame = v->getFrameForX(x); - FlexiNoteModel::PointList onPoints = - m_model->getPoints(frame); + EventVector local = m_model->getEventsCovering(frame); + if (!local.empty()) return local; - if (!onPoints.empty()) { - return onPoints; - } + int fuzz = ViewManager::scalePixelSize(2); + sv_frame_t start = v->getFrameForX(x - fuzz); + sv_frame_t end = v->getFrameForX(x + fuzz); - FlexiNoteModel::PointList prevPoints = - m_model->getPreviousPoints(frame); - FlexiNoteModel::PointList nextPoints = - m_model->getNextPoints(frame); + local = m_model->getEventsStartingWithin(frame, end - frame); + if (!local.empty()) return local; - FlexiNoteModel::PointList usePoints = prevPoints; + local = m_model->getEventsSpanning(start, frame - start); + if (!local.empty()) return local; - if (prevPoints.empty()) { - usePoints = nextPoints; - } else if (prevPoints.begin()->frame < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; - } else if (nextPoints.begin()->frame - frame < - frame - prevPoints.begin()->frame) { - usePoints = nextPoints; - } - - if (!usePoints.empty()) { - int fuzz = ViewManager::scalePixelSize(2); - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } - } - - return usePoints; + return {}; } bool -FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const +FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const { if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); - FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + EventVector onPoints = m_model->getEventsCovering(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; + for (const auto &p: onPoints) { + int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; - p = *i; + point = p; } } @@ -479,28 +445,23 @@ } bool -FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const +FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, Event &point) const { // GF: find the note that is closest to the cursor if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); - FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + EventVector onPoints = m_model->getEventsCovering(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; + for (const auto &p: onPoints) { + int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; - p = *i; + point = p; } } @@ -514,7 +475,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - FlexiNoteModel::PointList points = getLocalPoints(v, x); + EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { @@ -524,16 +485,17 @@ } } - FlexiNote note(0); - FlexiNoteModel::PointList::iterator i; + Event note(0); + EventVector::iterator i; for (i = points.begin(); i != points.end(); ++i) { - int y = getYForValue(v, i->value); + int y = getYForValue(v, i->getValue()); int h = NOTE_HEIGHT; // GF: larger notes if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + h = y - getYForValue + (v, i->getValue() + m_model->getValueQuantization()); if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; } @@ -546,17 +508,17 @@ if (i == points.end()) return tr("No local points"); - RealTime rt = RealTime::frame2RealTime(note.frame, + RealTime rt = RealTime::frame2RealTime(note.getFrame(), m_model->getSampleRate()); - RealTime rd = RealTime::frame2RealTime(note.duration, + RealTime rd = RealTime::frame2RealTime(note.getDuration(), m_model->getSampleRate()); QString pitchText; if (shouldConvertMIDIToHz()) { - int mnote = int(lrint(note.value)); - int cents = int(lrint((note.value - double(mnote)) * 100)); + int mnote = int(lrint(note.getValue())); + int cents = int(lrint((note.getValue() - double(mnote)) * 100)); double freq = Pitch::getFrequencyForPitch(mnote, cents); pitchText = tr("%1 (%2, %3 Hz)") .arg(Pitch::getPitchLabel(mnote, cents)) @@ -566,18 +528,18 @@ } else if (getScaleUnits() == "Hz") { pitchText = tr("%1 Hz (%2, %3)") - .arg(note.value) - .arg(Pitch::getPitchLabelForFrequency(note.value)) - .arg(Pitch::getPitchForFrequency(note.value)); + .arg(note.getValue()) + .arg(Pitch::getPitchLabelForFrequency(note.getValue())) + .arg(Pitch::getPitchForFrequency(note.getValue())); } else { pitchText = tr("%1 %2") - .arg(note.value).arg(getScaleUnits()); + .arg(note.getValue()).arg(getScaleUnits()); } QString text; - if (note.label == "") { + if (note.getLabel() == "") { text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) .arg(rt.toText(true).c_str()) .arg(pitchText) @@ -587,11 +549,11 @@ .arg(rt.toText(true).c_str()) .arg(pitchText) .arg(rd.toText(true).c_str()) - .arg(note.label); + .arg(note.getLabel()); } - pos = QPoint(v->getXForFrame(note.frame), - getYForValue(v, note.value)); + pos = QPoint(v->getXForFrame(note.getFrame()), + getYForValue(v, note.getValue())); return text; } @@ -605,41 +567,39 @@ } resolution = m_model->getResolution(); - FlexiNoteModel::PointList points; + EventVector points; if (snap == SnapNeighbouring) { points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; } - points = m_model->getPoints(frame, frame); + points = m_model->getEventsCovering(frame); sv_frame_t snapped = frame; bool found = false; - for (FlexiNoteModel::PointList::const_iterator i = points.begin(); + for (EventVector::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; + if (i->getFrame() > frame) { + snapped = i->getFrame(); found = true; break; - } else if (i->frame + i->duration >= frame) { - snapped = i->frame + i->duration; + } else if (i->getFrame() + i->getDuration() >= frame) { + snapped = i->getFrame() + i->getDuration(); found = true; break; } } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; + if (i->getFrame() <= frame) { + snapped = i->getFrame(); found = true; // don't break, as the next may be better } else { break; @@ -647,21 +607,21 @@ } else { // nearest - FlexiNoteModel::PointList::const_iterator j = i; + EventVector::const_iterator j = i; ++j; if (j == points.end()) { - snapped = i->frame; + snapped = i->getFrame(); found = true; break; - } else if (j->frame >= frame) { + } else if (j->getFrame() >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; + if (j->getFrame() - frame < frame - i->getFrame()) { + snapped = j->getFrame(); } else { - snapped = i->frame; + snapped = i->getFrame(); } found = true; break; @@ -806,10 +766,11 @@ // Profiler profiler("FlexiNoteLayer::paint", true); - int x1 = rect.right(); + int x0 = rect.left(), x1 = rect.right(); + sv_frame_t frame0 = v->getFrameForX(x0); sv_frame_t frame1 = v->getFrameForX(x1); - FlexiNoteModel::PointList points(m_model->getPoints(0, frame1)); + EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0)); if (points.empty()) return; paint.setPen(getBaseQColor()); @@ -825,7 +786,7 @@ if (max == min) max = min + 1.0; QPoint localPos; - FlexiNoteModel::Point illuminatePoint(0); + Event illuminatePoint(0); bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { @@ -838,19 +799,19 @@ int noteNumber = 0; - for (FlexiNoteModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { ++noteNumber; - const FlexiNoteModel::Point &p(*i); + const Event &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, p.value); - int w = v->getXForFrame(p.frame + p.duration) - x; + int x = v->getXForFrame(p.getFrame()); + int y = getYForValue(v, p.getValue()); + int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; int h = NOTE_HEIGHT; //GF: larger notes if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + h = y - getYForValue(v, p.getValue() + m_model->getValueQuantization()); if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes } @@ -858,46 +819,45 @@ paint.setPen(getBaseQColor()); paint.setBrush(brushColour); - if (shouldIlluminate && - // "illuminatePoint == p" - !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) && - !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) { + if (shouldIlluminate && illuminatePoint == p) { - paint.drawLine(x, -1, x, v->getPaintHeight() + 1); - paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1); + paint.drawLine(x, -1, x, v->getPaintHeight() + 1); + paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1); - paint.setPen(v->getForeground()); - // paint.setBrush(v->getForeground()); + paint.setPen(v->getForeground()); - QString vlabel = QString("freq: %1%2").arg(p.value).arg(m_model->getScaleUnits()); - // PaintAssistant::drawVisibleText(v, paint, - // x - paint.fontMetrics().width(vlabel) - 2, - // y + paint.fontMetrics().height()/2 - // - paint.fontMetrics().descent(), - // vlabel, PaintAssistant::OutlinedText); - PaintAssistant::drawVisibleText(v, paint, - x, - y - h/2 - 2 - paint.fontMetrics().height() - - paint.fontMetrics().descent(), - vlabel, PaintAssistant::OutlinedText); + QString vlabel = tr("freq: %1%2") + .arg(p.getValue()).arg(m_model->getScaleUnits()); + PaintAssistant::drawVisibleText + (v, paint, + x, + y - h/2 - 2 - paint.fontMetrics().height() + - paint.fontMetrics().descent(), + vlabel, PaintAssistant::OutlinedText); - QString hlabel = "dur: " + QString(RealTime::frame2RealTime - (p.duration, m_model->getSampleRate()).toText(true).c_str()); - PaintAssistant::drawVisibleText(v, paint, - x, - y - h/2 - paint.fontMetrics().descent() - 2, - hlabel, PaintAssistant::OutlinedText); + QString hlabel = tr("dur: %1") + .arg(RealTime::frame2RealTime + (p.getDuration(), m_model->getSampleRate()).toText(true) + .c_str()); + PaintAssistant::drawVisibleText + (v, paint, + x, + y - h/2 - paint.fontMetrics().descent() - 2, + hlabel, PaintAssistant::OutlinedText); - QString llabel = QString("%1").arg(p.label); - PaintAssistant::drawVisibleText(v, paint, - x, - y + h + 2 + paint.fontMetrics().descent(), - llabel, PaintAssistant::OutlinedText); - QString nlabel = QString("%1").arg(noteNumber); - PaintAssistant::drawVisibleText(v, paint, - x + paint.fontMetrics().averageCharWidth() / 2, - y + h/2 - paint.fontMetrics().descent(), - nlabel, PaintAssistant::OutlinedText); + QString llabel = QString("%1").arg(p.getLabel()); + PaintAssistant::drawVisibleText + (v, paint, + x, + y + h + 2 + paint.fontMetrics().descent(), + llabel, PaintAssistant::OutlinedText); + + QString nlabel = QString("%1").arg(noteNumber); + PaintAssistant::drawVisibleText + (v, paint, + x + paint.fontMetrics().averageCharWidth() / 2, + y + h/2 - paint.fontMetrics().descent(), + nlabel, PaintAssistant::OutlinedText); } paint.drawRect(x, y - h/2, w, h); @@ -923,7 +883,7 @@ void FlexiNoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model || m_model->getPoints().empty()) return; + if (!m_model || m_model->isEmpty()) return; QString unit; double min, max; @@ -971,13 +931,12 @@ double value = getValueForY(v, e->y()); - m_editingPoint = FlexiNoteModel::Point(frame, float(value), 0, 0.8f, tr("New Point")); + m_editingPoint = Event(frame, float(value), 0, 0.8f, 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_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point")); + m_editingCommand->add(m_editingPoint); m_editing = true; } @@ -995,7 +954,7 @@ double newValue = getValueForY(v, e->y()); - sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newFrame = m_editingPoint.getFrame(); sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; @@ -1004,11 +963,12 @@ newDuration = 1; } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = newFrame; - m_editingPoint.value = float(newValue); - m_editingPoint.duration = newDuration; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(newFrame) + .withValue(float(newValue)) + .withDuration(newDuration); + m_editingCommand->add(m_editingPoint); } void @@ -1048,14 +1008,12 @@ m_editing = false; - FlexiNoteModel::Point p(0); + Event p(0); if (!getPointToDrag(v, e->x(), e->y(), p)) return; - if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; + if (p.getFrame() != m_editingPoint.getFrame() || p.getValue() != m_editingPoint.getValue()) return; - m_editingCommand = new FlexiNoteModel::EditCommand(m_model, tr("Erase Point")); - - m_editingCommand->deletePoint(m_editingPoint); - + m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; @@ -1070,14 +1028,16 @@ if (!m_model) return; if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; - m_originalPoint = FlexiNote(m_editingPoint); + m_originalPoint = m_editingPoint; if (m_editMode == RightBoundary) { - m_dragPointX = v->getXForFrame(m_editingPoint.frame + m_editingPoint.duration); + m_dragPointX = v->getXForFrame + (m_editingPoint.getFrame() + m_editingPoint.getDuration()); } else { - m_dragPointX = v->getXForFrame(m_editingPoint.frame); + m_dragPointX = v->getXForFrame + (m_editingPoint.getFrame()); } - m_dragPointY = getYForValue(v, m_editingPoint.value); + m_dragPointY = getYForValue(v, m_editingPoint.getValue()); if (m_editingCommand) { finish(m_editingCommand); @@ -1088,27 +1048,31 @@ m_dragStartX = e->x(); m_dragStartY = e->y(); - sv_frame_t onset = m_originalPoint.frame; - sv_frame_t offset = m_originalPoint.frame + m_originalPoint.duration - 1; + sv_frame_t onset = m_originalPoint.getFrame(); + sv_frame_t offset = + m_originalPoint.getFrame() + + m_originalPoint.getDuration() - 1; m_greatestLeftNeighbourFrame = -1; m_smallestRightNeighbourFrame = std::numeric_limits::max(); + + EventVector allEvents = m_model->getAllEvents(); - for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); - i != m_model->getPoints().end(); ++i) { - FlexiNote currentNote = *i; + for (auto currentNote: allEvents) { // left boundary - if (currentNote.frame + currentNote.duration - 1 < onset) { - m_greatestLeftNeighbourFrame = currentNote.frame + currentNote.duration - 1; + if (currentNote.getFrame() + currentNote.getDuration() - 1 < onset) { + m_greatestLeftNeighbourFrame = + currentNote.getFrame() + currentNote.getDuration() - 1; } // right boundary - if (currentNote.frame > offset) { - m_smallestRightNeighbourFrame = currentNote.frame; + if (currentNote.getFrame() > offset) { + m_smallestRightNeighbourFrame = currentNote.getFrame(); break; } } + std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl; } @@ -1132,71 +1096,91 @@ double value = getValueForY(v, newy); if (!m_editingCommand) { - m_editingCommand = new FlexiNoteModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point")); } - - m_editingCommand->deletePoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); std::cerr << "edit mode: " << m_editMode << " intelligent actions = " << m_intelligentActions << std::endl; switch (m_editMode) { + case LeftBoundary : { // left - if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; + 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; + if (m_intelligentActions && + dragFrame >= m_originalPoint.getFrame() + m_originalPoint.getDuration()) { + dragFrame = m_originalPoint.getFrame() + m_originalPoint.getDuration() - 1; } - m_editingPoint.frame = dragFrame; - m_editingPoint.duration = m_originalPoint.frame - dragFrame + m_originalPoint.duration; + m_editingPoint = m_editingPoint + .withFrame(dragFrame) + .withDuration(m_originalPoint.getFrame() - + dragFrame + m_originalPoint.getDuration()); 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; + if (m_intelligentActions && + dragFrame <= m_greatestLeftNeighbourFrame) { + dragFrame = m_greatestLeftNeighbourFrame + 1; + } + if (m_intelligentActions && + dragFrame >= m_smallestRightNeighbourFrame) { + dragFrame = m_smallestRightNeighbourFrame - 1; + } + m_editingPoint = m_editingPoint + .withDuration(dragFrame - m_originalPoint.getFrame() + 1); break; } + case DragNote : { // left - if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; + 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; + if (m_intelligentActions && + dragFrame + m_originalPoint.getDuration() >= m_smallestRightNeighbourFrame) { + dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.getDuration(); } - m_editingPoint.frame = dragFrame; - - m_editingPoint.value = float(value); + + m_editingPoint = m_editingPoint + .withFrame(dragFrame) + .withValue(float(value)); // Re-analyse region within +/- 1 semitone of the dragged value float cents = 0; - int midiPitch = Pitch::getPitchForFrequency(m_editingPoint.value, ¢s); + int midiPitch = Pitch::getPitchForFrequency(m_editingPoint.getValue(), ¢s); double lower = Pitch::getFrequencyForPitch(midiPitch - 1, cents); double higher = Pitch::getFrequencyForPitch(midiPitch + 1, cents); - emit reAnalyseRegion(m_editingPoint.frame, - m_editingPoint.frame + m_editingPoint.duration, + emit reAnalyseRegion(m_editingPoint.getFrame(), + m_editingPoint.getFrame() + + m_editingPoint.getDuration(), float(lower), float(higher)); break; } + case SplitNote: // nothing break; } -// updateNoteValueFromPitchCurve(v, m_editingPoint); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->add(m_editingPoint); - std::cerr << "added new point(" << m_editingPoint.frame << "," << m_editingPoint.duration << ")" << std::endl; + std::cerr << "added new point(" << m_editingPoint.getFrame() << "," << m_editingPoint.getDuration() << ")" << std::endl; } void FlexiNoteLayer::editEnd(LayerGeometryProvider *v, QMouseEvent *e) { -// SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; - std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; + std::cerr << "FlexiNoteLayer::editEnd(" + << e->x() << "," << e->y() << ")" << std::endl; if (!m_model || !m_editing) return; @@ -1209,12 +1193,12 @@ emit materialiseReAnalysis(); } - m_editingCommand->deletePoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); updateNoteValueFromPitchCurve(v, m_editingPoint); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->add(m_editingPoint); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { + if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { + if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Edit Point"); } else { newName = tr("Relocate Point"); @@ -1241,8 +1225,8 @@ 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); + // m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); + // m_dragPointY = getYForValue(v, m_editingPoint.getValue()); if (m_editingCommand) { finish(m_editingCommand); @@ -1282,37 +1266,37 @@ void FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e) { - FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return; - FlexiNote note(*onPoints.begin()); + Event note(*onPoints.begin()); - FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Point")); - command->deletePoint(note); + command->remove(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); + Event newNote1(note.getFrame(), note.getValue(), + frame - note.getFrame() - gap, + note.getLevel(), note.getLabel()); - FlexiNote newNote2(frame, note.value, - note.duration - newNote1.duration, - note.level, note.label); + Event newNote2(frame, note.getValue(), + note.getDuration() - newNote1.getDuration(), + note.getLevel(), note.getLabel()); if (m_intelligentActions) { if (updateNoteValueFromPitchCurve(v, newNote1)) { - command->addPoint(newNote1); + command->add(newNote1); } if (updateNoteValueFromPitchCurve(v, newNote2)) { - command->addPoint(newNote2); + command->add(newNote2); } } else { - command->addPoint(newNote1); - command->addPoint(newNote2); + command->add(newNote1); + command->add(newNote2); } } @@ -1330,15 +1314,15 @@ sv_frame_t frame = v->getFrameForX(e->x()); double value = getValueForY(v, e->y()); - FlexiNoteModel::PointList noteList = m_model->getPoints(); + EventVector noteList = m_model->getAllEvents(); if (m_intelligentActions) { sv_frame_t smallestRightNeighbourFrame = 0; - for (FlexiNoteModel::PointList::const_iterator i = noteList.begin(); + for (EventVector::const_iterator i = noteList.begin(); i != noteList.end(); ++i) { - FlexiNote currentNote = *i; - if (currentNote.frame > frame) { - smallestRightNeighbourFrame = currentNote.frame; + Event currentNote = *i; + if (currentNote.getFrame() > frame) { + smallestRightNeighbourFrame = currentNote.getFrame(); break; } } @@ -1349,11 +1333,11 @@ } if (!m_intelligentActions || - (m_model->getPoints(frame).empty() && duration > 0)) { - FlexiNote newNote(frame, float(value), duration, 100.f, "new note"); - FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model->getEventsCovering(frame).empty() && duration > 0)) { + Event newNote(frame, float(value), duration, 100.f, tr("new note")); + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Add Point")); - command->addPoint(newNote); + command->add(newNote); finish(command); } } @@ -1388,33 +1372,33 @@ { if (!m_model) return; - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Snap Notes")); cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl; - for (FlexiNoteModel::PointList::iterator i = points.begin(); + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - FlexiNote note(*i); + Event note(*i); - cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.frame << " to " << note.frame + note.duration << endl; + cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.getFrame() << " to " << note.getFrame() + note.getDuration() << endl; - if (!s.contains(note.frame) && - !s.contains(note.frame + note.duration - 1)) { + if (!s.contains(note.getFrame()) && + !s.contains(note.getFrame() + note.getDuration() - 1)) { continue; } cerr << "snapSelectedNotesToPitchTrack: making new note" << endl; - FlexiNote newNote(note); + Event newNote(note); - command->deletePoint(note); + command->remove(note); if (updateNoteValueFromPitchCurve(v, newNote)) { - command->addPoint(newNote); + command->add(newNote); } } @@ -1424,69 +1408,61 @@ void FlexiNoteLayer::mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive) { - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); - - FlexiNoteModel::PointList::iterator i = points.begin(); + EventVector points; if (inclusive) { - while (i != points.end() && i->frame + i->duration < s.getStartFrame()) { - ++i; - } + points = m_model->getEventsSpanning(s.getStartFrame(), s.getDuration()); } else { - while (i != points.end() && i->frame < s.getStartFrame()) { - ++i; - } + points = m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); } + EventVector::iterator i = points.begin(); if (i == points.end()) return; - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Merge Notes")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Merge Notes")); - FlexiNote newNote(*i); + Event newNote(*i); while (i != points.end()) { if (inclusive) { - if (i->frame >= s.getEndFrame()) break; + if (i->getFrame() >= s.getEndFrame()) break; } else { - if (i->frame + i->duration > s.getEndFrame()) break; + if (i->getFrame() + i->getDuration() > s.getEndFrame()) break; } - newNote.duration = i->frame + i->duration - newNote.frame; - command->deletePoint(*i); + newNote = newNote.withDuration + (i->getFrame() + i->getDuration() - newNote.getFrame()); + command->remove(*i); ++i; } updateNoteValueFromPitchCurve(v, newNote); - command->addPoint(newNote); + command->add(newNote); finish(command); } bool -FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e) const +FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, Event ¬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); + EventVector dataPoints = + model->getEventsWithin(note.getFrame(), note.getDuration()); - std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl; + std::cerr << "frame " << note.getFrame() << ": " << dataPoints.size() << " candidate points" << std::endl; if (dataPoints.empty()) return false; std::vector pitchValues; - for (SparseModel::PointList::const_iterator i = + for (EventVector::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); - } + pitchValues.push_back(i->getValue()); } if (pitchValues.empty()) return false; @@ -1501,9 +1477,9 @@ median = pitchValues[size/2]; } - std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.value << " to median " << median << std::endl; + std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.getValue() << " to median " << median << std::endl; - note.value = float(median); + note = note.withValue(float(median)); return true; } @@ -1513,7 +1489,7 @@ { // GF: context sensitive cursors // v->getView()->setCursor(Qt::ArrowCursor); - FlexiNoteModel::Point note(0); + Event note(0); if (!getNoteToEdit(v, e->x(), e->y(), note)) { // v->getView()->setCursor(Qt::UpArrowCursor); return; @@ -1547,15 +1523,15 @@ } void -FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const +FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, Event ¬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 noteStartX = v->getXForFrame(note.getFrame()); + int noteEndX = v->getXForFrame(note.getFrame() + note.getDuration()); + int noteValueY = getYForValue(v,note.getValue()); int noteStartY = noteValueY - (NOTE_HEIGHT / 2); int noteEndY = noteValueY + (NOTE_HEIGHT / 2); @@ -1581,10 +1557,10 @@ std::cerr << "Opening note editor dialog" << std::endl; if (!m_model) return false; - FlexiNoteModel::Point note(0); + Event note(0); if (!getPointToDrag(v, e->x(), e->y(), note)) return false; -// FlexiNoteModel::Point note = *points.begin(); +// Event note = *points.begin(); ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), @@ -1594,23 +1570,23 @@ ItemEditDialog::ShowText, getScaleUnits()); - dialog->setFrameTime(note.frame); - dialog->setValue(note.value); - dialog->setFrameDuration(note.duration); - dialog->setText(note.label); + dialog->setFrameTime(note.getFrame()); + dialog->setValue(note.getValue()); + dialog->setFrameDuration(note.getDuration()); + dialog->setText(note.getLabel()); 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(); + Event newNote = note + .withFrame(dialog->getFrameTime()) + .withValue(dialog->getValue()) + .withDuration(dialog->getFrameDuration()) + .withLabel(dialog->getText()); - FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Point")); - command->deletePoint(note); - command->addPoint(newNote); + command->remove(note); + command->add(newNote); finish(command); } @@ -1623,21 +1599,17 @@ { if (!m_model) return; - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - 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); - } + for (Event p: points) { + command->remove(p); + Event moved = p.withFrame(p.getFrame() + + newStartFrame - s.getStartFrame()); + command->add(moved); } finish(command); @@ -1646,37 +1618,28 @@ void FlexiNoteLayer::resizeSelection(Selection s, Selection newSize) { - if (!m_model) return; + if (!m_model || !s.getDuration()) return; - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); + + for (Event p: points) { - for (FlexiNoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; + double newDuration = double(p.getDuration()) * ratio; - if (s.contains(i->frame)) { - - double targetStart = double(i->frame); - targetStart = double(newSize.getStartFrame()) + - targetStart - double(s.getStartFrame()) * ratio; - - double targetEnd = double(i->frame + i->duration); - targetEnd = double(newSize.getStartFrame()) + - targetEnd - double(s.getStartFrame()) * ratio; - - FlexiNoteModel::Point newPoint(*i); - newPoint.frame = lrint(targetStart); - newPoint.duration = lrint(targetEnd - targetStart); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)) + .withDuration(lrint(newDuration)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -1687,18 +1650,14 @@ { if (!m_model) return; - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selected Points")); - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (FlexiNoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - command->deletePoint(*i); - } + for (Event p: points) { + command->remove(p); } finish(command); @@ -1709,21 +1668,14 @@ { if (!m_model) return; - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selected Points")); - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsSpanning(s.getStartFrame(), s.getDuration()); - for (FlexiNoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - bool overlap = !( - ((s.getStartFrame() <= i->frame) && (s.getEndFrame() <= i->frame)) || // selection is left of note - ((s.getStartFrame() >= (i->frame+i->duration)) && (s.getEndFrame() >= (i->frame+i->duration))) // selection is right of note - ); - if (overlap) { - command->deletePoint(*i); - } + for (Event p: points) { + command->remove(p); } finish(command); @@ -1734,16 +1686,11 @@ { if (!m_model) return; - FlexiNoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - 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); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -1752,7 +1699,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + const EventVector &points = from.getPoints(); bool realign = false; @@ -1773,13 +1720,12 @@ } } - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; sv_frame_t frame = 0; if (!realign) { @@ -1788,7 +1734,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -1796,32 +1742,29 @@ } } - 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 { + Event p = *i; + Event newPoint = p; + if (!p.hasValue()) { + newPoint = newPoint.withValue((m_model->getValueMinimum() + + m_model->getValueMaximum()) / 2); + } + if (!p.hasDuration()) { sv_frame_t nextFrame = frame; - Clipboard::PointList::const_iterator j = i; + EventVector::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(); + newPoint = newPoint.withDuration(m_model->getResolution()); } else { - newPoint.duration = nextFrame - frame; + newPoint = newPoint.withDuration(nextFrame - frame); } } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); @@ -1831,21 +1774,25 @@ void FlexiNoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) { - m_pendingNoteOns.insert(FlexiNote(frame, float(pitch), 0, float(velocity / 127.0), "")); + m_pendingNoteOns.insert(Event(frame, float(pitch), 0, + float(velocity / 127.0), "")); } void FlexiNoteLayer::addNoteOff(sv_frame_t frame, int pitch) { - for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin(); + for (NoteSet::iterator i = m_pendingNoteOns.begin(); i != m_pendingNoteOns.end(); ++i) { - if (lrint((*i).value) == pitch) { - FlexiNote note(*i); + + Event p = *i; + + if (lrintf(p.getValue()) == pitch) { m_pendingNoteOns.erase(i); - note.duration = frame - note.frame; + Event note = p.withDuration(frame - p.getFrame()); if (m_model) { - FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand - (m_model, note, tr("Record FlexiNote")); + ChangeEventsCommand *c = new ChangeEventsCommand + (m_model, tr("Record Note")); + c->add(note); // execute and bundle: CommandHistory::getInstance()->addCommand(c, true, true); } @@ -1888,11 +1835,6 @@ VerticalScale scale = (VerticalScale) attributes.value("verticalScale").toInt(&ok); if (ok) setVerticalScale(scale); - -// bool alsoOk; -// double min = attributes.value("scaleMinimum").toDouble(&ok); -// double max = attributes.value("scaleMaximum").toDouble(&alsoOk); -// if (ok && alsoOk && min != max) setDisplayExtents(min, max); } void @@ -1901,12 +1843,13 @@ double minf = std::numeric_limits::max(); double maxf = 0; bool hasNotes = 0; - for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); - i != m_model->getPoints().end(); ++i) { + EventVector allPoints = m_model->getAllEvents(); + for (EventVector::const_iterator i = allPoints.begin(); + i != allPoints.end(); ++i) { hasNotes = 1; - FlexiNote note = *i; - if (note.value < minf) minf = note.value; - if (note.value > maxf) maxf = note.value; + Event note = *i; + if (note.getValue() < minf) minf = note.getValue(); + if (note.getValue() > maxf) maxf = note.getValue(); } std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl; diff -r 8d5bf4ab98ef -r 42c87368287c layer/FlexiNoteLayer.h --- a/layer/FlexiNoteLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/FlexiNoteLayer.h Fri May 17 10:02:52 2019 +0100 @@ -16,12 +16,10 @@ #ifndef SV_FLEXINOTE_LAYER_H #define SV_FLEXINOTE_LAYER_H -#define NOTE_HEIGHT 16 - #include "SingleColourLayer.h" #include "VerticalScaleLayer.h" -#include "data/model/FlexiNoteModel.h" +#include "data/model/NoteModel.h" #include #include @@ -84,7 +82,7 @@ void mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive); const Model *getModel() const override { return m_model; } - void setModel(FlexiNoteModel *model); + void setModel(NoteModel *model); PropertyList getProperties() const override; QString getPropertyLabel(const PropertyName &) const override; @@ -173,39 +171,39 @@ int getDefaultColourHint(bool dark, bool &impose) override; - FlexiNoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int) const; - bool getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const; - bool getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const; - void getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const; + bool getNoteToEdit(LayerGeometryProvider *v, int x, int y, Event &) const; + void getRelativeMousePosition(LayerGeometryProvider *v, Event ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const; SparseTimeValueModel *getAssociatedPitchModel(LayerGeometryProvider *v) const; - bool updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e) const; + bool updateNoteValueFromPitchCurve(LayerGeometryProvider *v, Event ¬e) const; void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e); - FlexiNoteModel *m_model; + NoteModel *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; + Event m_originalPoint; + Event m_editingPoint; sv_frame_t m_greatestLeftNeighbourFrame; sv_frame_t m_smallestRightNeighbourFrame; - FlexiNoteModel::EditCommand *m_editingCommand; + ChangeEventsCommand *m_editingCommand; VerticalScale m_verticalScale; EditMode m_editMode; - typedef std::set FlexiNoteSet; - FlexiNoteSet m_pendingNoteOns; + typedef std::set NoteSet; + NoteSet m_pendingNoteOns; mutable double m_scaleMinimum; mutable double m_scaleMaximum; bool shouldAutoAlign() const; - void finish(FlexiNoteModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/ImageLayer.cpp --- a/layer/ImageLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/ImageLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -46,8 +46,6 @@ Layer(), m_model(nullptr), m_editing(false), - m_originalPoint(0, "", ""), - m_editingPoint(0, "", ""), m_editingCommand(nullptr) { } @@ -121,27 +119,25 @@ return true; } - -ImageModel::PointList +EventVector ImageLayer::getLocalPoints(LayerGeometryProvider *v, int x, int ) const { - if (!m_model) return ImageModel::PointList(); + if (!m_model) return {}; // SVDEBUG << "ImageLayer::getLocalPoints(" << x << "," << y << "):"; - const ImageModel::PointList &points(m_model->getPoints()); + EventVector points(m_model->getAllEvents()); - ImageModel::PointList rv; + EventVector rv; - for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ) { + for (EventVector::const_iterator i = points.begin(); i != points.end(); ) { - const ImageModel::Point &p(*i); - int px = v->getXForFrame(p.frame); + Event p(*i); + int px = v->getXForFrame(p.getFrame()); if (px > x) break; ++i; if (i != points.end()) { - int nx = v->getXForFrame((*i).frame); + int nx = v->getXForFrame(i->getFrame()); if (nx < x) { // as we aim not to overlap the images, if the following // image begins to the left of a point then the current @@ -153,13 +149,13 @@ // this image is a candidate, test it properly int width = 32; - if (m_scaled[v].find(p.image) != m_scaled[v].end()) { - width = m_scaled[v][p.image].width(); + if (m_scaled[v].find(p.getURI()) != m_scaled[v].end()) { + width = m_scaled[v][p.getURI()].width(); // SVDEBUG << "scaled width = " << width << endl; } if (x >= px && x < px + width) { - rv.insert(p); + rv.push_back(p); } } @@ -175,7 +171,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - ImageModel::PointList points = getLocalPoints(v, x, pos.y()); + EventVector points = getLocalPoints(v, x, pos.y()); if (points.empty()) { if (!m_model->isReady()) { @@ -209,74 +205,33 @@ bool ImageLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const + int &resolution, + SnapType snap) const { if (!m_model) { return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); - ImageModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame), -1); + EventVector points = getLocalPoints(v, v->getXForFrame(frame), -1); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; } - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; - - for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - - if (snap == SnapRight) { - - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } - - } else { // nearest - - ImageModel::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; - } - } + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event) { return true; }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e)) { + frame = e.getFrame(); + return true; } - frame = snapped; - return found; + return false; } void @@ -295,7 +250,7 @@ sv_frame_t frame0 = v->getFrameForX(x0); sv_frame_t frame1 = v->getFrameForX(x1); - ImageModel::PointList points(m_model->getPoints(frame0, frame1)); + EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 2)); if (points.empty()) return; paint.save(); @@ -315,18 +270,18 @@ paint.setBrush(brushColour); paint.setRenderHint(QPainter::Antialiasing, true); - for (ImageModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const ImageModel::Point &p(*i); + Event p(*i); - int x = v->getXForFrame(p.frame); + int x = v->getXForFrame(p.getFrame()); int nx = x + 2000; - ImageModel::PointList::const_iterator j = i; + EventVector::const_iterator j = i; ++j; if (j != points.end()) { - int jx = v->getXForFrame(j->frame); + int jx = v->getXForFrame(j->getFrame()); if (jx < nx) nx = jx; } @@ -338,11 +293,11 @@ } void -ImageLayer::drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p, +ImageLayer::drawImage(LayerGeometryProvider *v, QPainter &paint, const Event &p, int x, int nx) const { - QString label = p.label; - QString imageName = p.image; + QString label = p.getLabel(); + QString imageName = p.getURI(); QImage image; QString additionalText; @@ -567,12 +522,12 @@ if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - m_editingPoint = ImageModel::Point(frame, "", ""); + m_editingPoint = Event(frame); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new ImageModel::EditCommand(m_model, "Add Image"); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand = new ChangeEventsCommand(m_model, "Add Image"); + m_editingCommand->add(m_editingPoint); m_editing = true; } @@ -588,9 +543,10 @@ if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame); + m_editingCommand->add(m_editingPoint); } void @@ -601,16 +557,16 @@ ImageDialog dialog(tr("Select image"), "", ""); + m_editingCommand->remove(m_editingPoint); + if (dialog.exec() == QDialog::Accepted) { checkAddSource(dialog.getImage()); - ImageModel::ChangeImageCommand *command = - new ImageModel::ChangeImageCommand - (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel()); - m_editingCommand->addCommand(command); - } else { - m_editingCommand->deletePoint(m_editingPoint); + m_editingPoint = m_editingPoint + .withURI(dialog.getImage()) + .withLabel(dialog.getLabel()); + m_editingCommand->add(m_editingPoint); } finish(m_editingCommand); @@ -629,10 +585,10 @@ return false; } - ImageModel::Point point(frame, url, ""); - ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, "Add Image"); - command->addPoint(point); + Event point = Event(frame).withURI(url); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, "Add Image"); + command->add(point); finish(command); return true; } @@ -644,7 +600,7 @@ if (!m_model) return; - ImageModel::PointList points = getLocalPoints(v, e->x(), e->y()); + EventVector points = getLocalPoints(v, e->x(), e->y()); if (points.empty()) return; m_editOrigin = e->pos(); @@ -665,18 +621,19 @@ if (!m_model || !m_editing) return; sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); - sv_frame_t frame = m_originalPoint.frame + frameDiff; + sv_frame_t frame = m_originalPoint.getFrame() + frameDiff; if (frame < 0) frame = 0; frame = (frame / m_model->getResolution()) * m_model->getResolution(); if (!m_editingCommand) { - m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Move Image")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame); + m_editingCommand->add(m_editingPoint); } void @@ -698,11 +655,11 @@ { if (!m_model) return false; - ImageModel::PointList points = getLocalPoints(v, e->x(), e->y()); + EventVector points = getLocalPoints(v, e->x(), e->y()); if (points.empty()) return false; - QString image = points.begin()->image; - QString label = points.begin()->label; + QString image = points.begin()->getURI(); + QString label = points.begin()->getLabel(); ImageDialog dialog(tr("Select image"), image, @@ -712,11 +669,12 @@ checkAddSource(dialog.getImage()); - ImageModel::ChangeImageCommand *command = - new ImageModel::ChangeImageCommand - (m_model, *points.begin(), dialog.getImage(), dialog.getLabel()); - - CommandHistory::getInstance()->addCommand(command); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Edit Image")); + command->remove(*points.begin()); + command->add(points.begin()-> + withURI(dialog.getImage()).withLabel(dialog.getLabel())); + finish(command); } return true; @@ -727,21 +685,17 @@ { if (!m_model) return; - ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - ImageModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + for (Event p: points) { + command->remove(p); + Event moved = p.withFrame(p.getFrame() + + newStartFrame - s.getStartFrame()); + command->add(moved); } finish(command); @@ -752,30 +706,24 @@ { if (!m_model) return; - ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); + + for (Event p: points) { - for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; - if (s.contains(i->frame)) { - - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; - - ImageModel::Point newPoint(*i); - newPoint.frame = lrint(target); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -786,15 +734,14 @@ { if (!m_model) return; - ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Delete Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selection")); - ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + for (Event p: points) { + command->remove(p); } finish(command); @@ -805,16 +752,11 @@ { if (!m_model) return; - ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->label); - point.setReferenceFrame(alignToReference(v, i->frame)); - to.addPoint(point); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -823,7 +765,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + const EventVector &points = from.getPoints(); bool realign = false; @@ -844,14 +786,12 @@ } } - ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; - sv_frame_t frame = 0; if (!realign) { @@ -860,7 +800,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -868,19 +808,20 @@ } } - ImageModel::Point newPoint(frame); + Event p = *i; + Event newPoint = p; //!!! inadequate - if (i->haveLabel()) { - newPoint.label = i->getLabel(); - } else if (i->haveValue()) { - newPoint.label = QString("%1").arg(i->getValue()); - } else { - newPoint.label = tr("New Point"); + if (!p.hasLabel()) { + if (p.hasValue()) { + newPoint = newPoint.withLabel(QString("%1").arg(p.getValue())); + } else { + newPoint = newPoint.withLabel(tr("New Point")); + } } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); @@ -922,12 +863,12 @@ void ImageLayer::checkAddSources() { - const ImageModel::PointList &points(m_model->getPoints()); + const EventVector &points(m_model->getAllEvents()); - for (ImageModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - checkAddSource((*i).image); + checkAddSource((*i).getURI()); } } diff -r 8d5bf4ab98ef -r 42c87368287c layer/ImageLayer.h --- a/layer/ImageLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/ImageLayer.h Fri May 17 10:02:52 2019 +0100 @@ -105,12 +105,12 @@ void fileSourceReady(); protected: - ImageModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int x, int y) const; bool getImageOriginalSize(QString name, QSize &size) const; QImage getImage(LayerGeometryProvider *v, QString name, QSize maxSize) const; - void drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p, + void drawImage(LayerGeometryProvider *v, QPainter &paint, const Event &p, int x, int nx) const; //!!! how to reap no-longer-used images? @@ -130,11 +130,11 @@ ImageModel *m_model; bool m_editing; QPoint m_editOrigin; - ImageModel::Point m_originalPoint; - ImageModel::Point m_editingPoint; - ImageModel::EditCommand *m_editingCommand; + Event m_originalPoint; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; - void finish(ImageModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/Layer.cpp --- a/layer/Layer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/Layer.cpp Fri May 17 10:02:52 2019 +0100 @@ -232,7 +232,7 @@ // We either want to be literal all the way through, or aligned // all the way through. - for (Clipboard::PointList::const_iterator i = clip.getPoints().begin(); + for (EventVector::const_iterator i = clip.getPoints().begin(); i != clip.getPoints().end(); ++i) { // In principle, we want to know whether the aligned version @@ -252,12 +252,12 @@ sv_frame_t sourceFrame = i->getFrame(); sv_frame_t referenceFrame = sourceFrame; - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { referenceFrame = i->getReferenceFrame(); } sv_frame_t myMappedFrame = alignToReference(v, sourceFrame); -// cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->haveReferenceFrame() << "), myMappedFrame = " << myMappedFrame << endl; +// cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->hasReferenceFrame() << "), myMappedFrame = " << myMappedFrame << endl; if (myMappedFrame != referenceFrame) return true; } @@ -647,9 +647,9 @@ stream << QString("getLayerTypeName (LayerFactory::getInstance()->getLayerType(this)))) - .arg(getObjectExportId(this)) + .arg(getExportId()) .arg(encodeEntities(objectName())) - .arg(getObjectExportId(getModel())) + .arg(getModel() ? getModel()->getExportId() : -1) .arg(extraAttributes); if (m_measureRects.empty()) { @@ -681,9 +681,9 @@ stream << QString("\n") .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName (LayerFactory::getInstance()->getLayerType(this)))) - .arg(getObjectExportId(this)) + .arg(getExportId()) .arg(encodeEntities(objectName())) - .arg(getObjectExportId(getModel())) + .arg(getModel() ? getModel()->getExportId() : -1) .arg(extraAttributes); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/Layer.h --- a/layer/Layer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/Layer.h Fri May 17 10:02:52 2019 +0100 @@ -161,7 +161,6 @@ enum SnapType { SnapLeft, SnapRight, - SnapNearest, SnapNeighbouring }; @@ -171,13 +170,12 @@ * * If snap is SnapLeft or SnapRight, adjust the frame to match * that of the nearest feature in the given direction regardless - * of how far away it is. If snap is SnapNearest, adjust the - * frame to that of the nearest feature in either direction. If - * snap is SnapNeighbouring, adjust the frame to that of the - * nearest feature if it is close, and leave it alone (returning - * false) otherwise. SnapNeighbouring should always choose the - * same feature that would be used in an editing operation through - * calls to editStart etc. + * of how far away it is. If snap is SnapNeighbouring, adjust the + * frame to that of the nearest feature in either direction if it + * is close, and leave it alone (returning false) otherwise. + * SnapNeighbouring should always choose the same feature that + * would be used in an editing operation through calls to + * editStart etc. * * Return true if a suitable feature was found and frame adjusted * accordingly. Return false if no suitable feature was available @@ -551,6 +549,10 @@ virtual bool canExistWithoutModel() const { return false; } public slots: + /** + * Change the visibility status (dormancy) of the layer in the + * given view. + */ void showLayer(LayerGeometryProvider *, bool show); signals: diff -r 8d5bf4ab98ef -r 42c87368287c layer/LayerFactory.cpp --- a/layer/LayerFactory.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/LayerFactory.cpp Fri May 17 10:02:52 2019 +0100 @@ -37,7 +37,6 @@ #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" @@ -162,12 +161,12 @@ } if (dynamic_cast(model)) { - types.insert(Notes); - } - - // NOTE: GF: types is a set, so order of insertion does not matter - if (dynamic_cast(model)) { - types.insert(FlexiNotes); + NoteModel *nm = dynamic_cast(model); + if (nm->getSubtype() == NoteModel::FLEXI_NOTE) { + types.insert(FlexiNotes); + } else { + types.insert(Notes); + } } if (dynamic_cast(model)) { @@ -327,8 +326,7 @@ if (trySetModel(layer, model)) return; - // GF: added FlexiNoteLayer - if (trySetModel(layer, model)) + if (trySetModel(layer, model)) return; if (trySetModel(layer, model)) @@ -361,7 +359,7 @@ } else if (layerType == TimeValues) { return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true); } else if (layerType == FlexiNotes) { - return new FlexiNoteModel(baseModel->getSampleRate(), 1, true); + return new NoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Notes) { return new NoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Regions) { @@ -499,69 +497,75 @@ settings.beginGroup("LayerDefaults"); QString defaults = settings.value(getLayerTypeName(type), "").toString(); if (defaults == "") return; + setLayerProperties(layer, defaults); + settings.endGroup(); +} -// cerr << "defaults=\"" << defaults << "\"" << endl; +void +LayerFactory::setLayerProperties(Layer *layer, QString newXml) +{ + QDomDocument docOld, docNew; + QString oldXml = layer->toXmlString(); - QString xml = layer->toXmlString(); - QDomDocument docOld, docNew; - - if (docOld.setContent(xml, false) && - docNew.setContent(defaults, false)) { + if (!docOld.setContent(oldXml, false)) { + SVCERR << "LayerFactory::setLayerProperties: Failed to parse XML for existing layer properties! XML string is: " << oldXml << endl; + return; + } + + if (!docNew.setContent(newXml, false)) { + SVCERR << "LayerFactory::setLayerProperties: Failed to parse XML: " << newXml << endl; + return; + } - QXmlAttributes attrs; + QXmlAttributes attrs; - QDomElement layerElt = docNew.firstChildElement("layer"); - QDomNamedNodeMap attrNodes = layerElt.attributes(); + QDomElement layerElt = docNew.firstChildElement("layer"); + QDomNamedNodeMap attrNodes = layerElt.attributes(); - for (int i = 0; i < attrNodes.length(); ++i) { - QDomAttr attr = attrNodes.item(i).toAttr(); - if (attr.isNull()) continue; + for (int i = 0; i < attrNodes.length(); ++i) { + QDomAttr attr = attrNodes.item(i).toAttr(); + if (attr.isNull()) continue; // cerr << "append \"" << attr.name() // << "\" -> \"" << attr.value() << "\"" // << endl; - attrs.append(attr.name(), "", "", attr.value()); - } - - layerElt = docOld.firstChildElement("layer"); - attrNodes = layerElt.attributes(); - for (int i = 0; i < attrNodes.length(); ++i) { - QDomAttr attr = attrNodes.item(i).toAttr(); - if (attr.isNull()) continue; - if (attrs.value(attr.name()) == "") { + attrs.append(attr.name(), "", "", attr.value()); + } + + layerElt = docOld.firstChildElement("layer"); + attrNodes = layerElt.attributes(); + for (int i = 0; i < attrNodes.length(); ++i) { + QDomAttr attr = attrNodes.item(i).toAttr(); + if (attr.isNull()) continue; + if (attrs.value(attr.name()) == "") { // cerr << "append \"" << attr.name() // << "\" -> \"" << attr.value() << "\"" // << endl; - attrs.append(attr.name(), "", "", attr.value()); - } + attrs.append(attr.name(), "", "", attr.value()); } - - layer->setProperties(attrs); } - - settings.endGroup(); + + layer->setProperties(attrs); } LayerFactory::LayerType LayerFactory::getLayerTypeForClipboardContents(const Clipboard &clip) { - const Clipboard::PointList &contents = clip.getPoints(); + const EventVector &contents = clip.getPoints(); - bool haveFrame = false; bool haveValue = false; bool haveDuration = false; bool haveLevel = false; - for (Clipboard::PointList::const_iterator i = contents.begin(); + for (EventVector::const_iterator i = contents.begin(); i != contents.end(); ++i) { - if (i->haveFrame()) haveFrame = true; - if (i->haveValue()) haveValue = true; - if (i->haveDuration()) haveDuration = true; - if (i->haveLevel()) haveLevel = true; + if (i->hasValue()) haveValue = true; + if (i->hasDuration()) haveDuration = true; + if (i->hasLevel()) haveLevel = true; } - if (haveFrame && haveValue && haveDuration && haveLevel) return Notes; - if (haveFrame && haveValue && haveDuration) return Regions; - if (haveFrame && haveValue) return TimeValues; + if (haveValue && haveDuration && haveLevel) return Notes; + if (haveValue && haveDuration) return Regions; + if (haveValue) return TimeValues; return TimeInstants; } diff -r 8d5bf4ab98ef -r 42c87368287c layer/LayerFactory.h --- a/layer/LayerFactory.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/LayerFactory.h Fri May 17 10:02:52 2019 +0100 @@ -68,8 +68,20 @@ Layer *createLayer(LayerType type); + /** + * Set the default properties of a layer, from the XML string + * contained in the LayerDefaults settings group for the given + * layer type. Leave unchanged any properties not mentioned in the + * settings. + */ void setLayerDefaultProperties(LayerType type, Layer *layer); + /** + * Set the properties of a layer, from the XML string + * provided. Leave unchanged any properties not mentioned. + */ + void setLayerProperties(Layer *layer, QString xmlString); + QString getLayerPresentationName(LayerType type); bool isLayerSliceable(const Layer *); diff -r 8d5bf4ab98ef -r 42c87368287c layer/NoteLayer.cpp --- a/layer/NoteLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/NoteLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -390,71 +390,46 @@ return mapper; } -NoteModel::PointList +EventVector NoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { - if (!m_model) return NoteModel::PointList(); - + if (!m_model) return {}; + sv_frame_t frame = v->getFrameForX(x); - NoteModel::PointList onPoints = - m_model->getPoints(frame); + EventVector local = m_model->getEventsCovering(frame); + if (!local.empty()) return local; - if (!onPoints.empty()) { - return onPoints; - } + int fuzz = ViewManager::scalePixelSize(2); + sv_frame_t start = v->getFrameForX(x - fuzz); + sv_frame_t end = v->getFrameForX(x + fuzz); - NoteModel::PointList prevPoints = - m_model->getPreviousPoints(frame); - NoteModel::PointList nextPoints = - m_model->getNextPoints(frame); + local = m_model->getEventsStartingWithin(frame, end - frame); + if (!local.empty()) return local; - NoteModel::PointList usePoints = prevPoints; + local = m_model->getEventsSpanning(start, frame - start); + if (!local.empty()) return local; - if (prevPoints.empty()) { - usePoints = nextPoints; - } else if (int(prevPoints.begin()->frame) < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; - } else if (int(nextPoints.begin()->frame) - frame < - frame - int(prevPoints.begin()->frame)) { - usePoints = nextPoints; - } - - if (!usePoints.empty()) { - int fuzz = ViewManager::scalePixelSize(2); - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } - } - - return usePoints; + return {}; } bool -NoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &p) const +NoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const { if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); - NoteModel::PointList onPoints = m_model->getPoints(frame); + EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return false; -// cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << endl; - int nearestDistance = -1; - - for (NoteModel::PointList::const_iterator i = onPoints.begin(); - i != onPoints.end(); ++i) { - - int distance = getYForValue(v, (*i).value) - y; + for (const auto &p: onPoints) { + int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; - p = *i; + point = p; } } @@ -468,7 +443,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - NoteModel::PointList points = getLocalPoints(v, x); + EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { @@ -478,16 +453,17 @@ } } - Note note(0); - NoteModel::PointList::iterator i; + Event note; + EventVector::iterator i; for (i = points.begin(); i != points.end(); ++i) { - int y = getYForValue(v, i->value); + int y = getYForValue(v, i->getValue()); int h = 3; if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + h = y - getYForValue + (v, i->getValue() + m_model->getValueQuantization()); if (h < 3) h = 3; } @@ -499,17 +475,19 @@ if (i == points.end()) return tr("No local points"); - RealTime rt = RealTime::frame2RealTime(note.frame, + RealTime rt = RealTime::frame2RealTime(note.getFrame(), m_model->getSampleRate()); - RealTime rd = RealTime::frame2RealTime(note.duration, + RealTime rd = RealTime::frame2RealTime(note.getDuration(), m_model->getSampleRate()); QString pitchText; + float value = note.getValue(); + if (shouldConvertMIDIToHz()) { - int mnote = int(lrint(note.value)); - int cents = int(lrint((note.value - float(mnote)) * 100)); + int mnote = int(lrint(value)); + int cents = int(lrint((value - float(mnote)) * 100)); double freq = Pitch::getFrequencyForPitch(mnote, cents); pitchText = tr("%1 (%2, %3 Hz)") .arg(Pitch::getPitchLabel(mnote, cents)) @@ -519,18 +497,18 @@ } else if (getScaleUnits() == "Hz") { pitchText = tr("%1 Hz (%2, %3)") - .arg(note.value) - .arg(Pitch::getPitchLabelForFrequency(note.value)) - .arg(Pitch::getPitchForFrequency(note.value)); + .arg(value) + .arg(Pitch::getPitchLabelForFrequency(value)) + .arg(Pitch::getPitchForFrequency(value)); } else { pitchText = tr("%1 %2") - .arg(note.value).arg(getScaleUnits()); + .arg(value).arg(getScaleUnits()); } QString text; - if (note.label == "") { + if (note.getLabel() == "") { text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) .arg(rt.toText(true).c_str()) .arg(pitchText) @@ -540,11 +518,10 @@ .arg(rt.toText(true).c_str()) .arg(pitchText) .arg(rd.toText(true).c_str()) - .arg(note.label); + .arg(note.getLabel()); } - pos = QPoint(v->getXForFrame(note.frame), - getYForValue(v, note.value)); + pos = QPoint(v->getXForFrame(note.getFrame()), getYForValue(v, value)); return text; } @@ -557,67 +534,33 @@ return Layer::snapToFeatureFrame(v, frame, resolution, snap); } + // SnapLeft / SnapRight: return frame of nearest feature in that + // direction no matter how far away + // + // SnapNeighbouring: return frame of feature that would be used in + // an editing operation, i.e. closest feature in either direction + // but only if it is "close enough" + resolution = m_model->getResolution(); - NoteModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); + EventVector points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; } - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; - - for (NoteModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - - if (snap == SnapRight) { - - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } - - } else { // nearest - - NoteModel::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; - } - } + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event) { return true; }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e)) { + frame = e.getFrame(); + return true; } - frame = snapped; - return found; + return false; } void @@ -756,7 +699,7 @@ sv_frame_t frame0 = v->getFrameForX(x0); sv_frame_t frame1 = v->getFrameForX(x1); - NoteModel::PointList points(m_model->getPoints(frame0, frame1)); + EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0)); if (points.empty()) return; paint.setPen(getBaseQColor()); @@ -772,7 +715,7 @@ if (max == min) max = min + 1.0; QPoint localPos; - NoteModel::Point illuminatePoint(0); + Event illuminatePoint; bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { @@ -786,18 +729,18 @@ paint.save(); paint.setRenderHint(QPainter::Antialiasing, false); - for (NoteModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const NoteModel::Point &p(*i); + const Event &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, p.value); - int w = v->getXForFrame(p.frame + p.duration) - x; + int x = v->getXForFrame(p.getFrame()); + int y = getYForValue(v, p.getValue()); + int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; int h = 3; if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + h = y - getYForValue(v, p.getValue() + m_model->getValueQuantization()); if (h < 3) h = 3; } @@ -805,15 +748,12 @@ paint.setPen(getBaseQColor()); paint.setBrush(brushColour); - if (shouldIlluminate && - // "illuminatePoint == p" - !NoteModel::Point::Comparator()(illuminatePoint, p) && - !NoteModel::Point::Comparator()(p, illuminatePoint)) { + if (shouldIlluminate && illuminatePoint == p) { paint.setPen(v->getForeground()); paint.setBrush(v->getForeground()); - QString vlabel = QString("%1%2").arg(p.value).arg(getScaleUnits()); + QString vlabel = QString("%1%2").arg(p.getValue()).arg(getScaleUnits()); PaintAssistant::drawVisibleText(v, paint, x - paint.fontMetrics().width(vlabel) - 2, y + paint.fontMetrics().height()/2 @@ -821,7 +761,7 @@ vlabel, PaintAssistant::OutlinedText); QString hlabel = RealTime::frame2RealTime - (p.frame, m_model->getSampleRate()).toText(true).c_str(); + (p.getFrame(), m_model->getSampleRate()).toText(true).c_str(); PaintAssistant::drawVisibleText(v, paint, x, y - h/2 - paint.fontMetrics().descent() - 2, @@ -855,7 +795,7 @@ void NoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model || m_model->getPoints().empty()) return; + if (!m_model || m_model->isEmpty()) return; QString unit; double min, max; @@ -903,13 +843,12 @@ double value = getValueForY(v, e->y()); - m_editingPoint = NoteModel::Point(frame, float(value), 0, 0.8f, tr("New Point")); + m_editingPoint = Event(frame, float(value), 0, 0.8f, tr("New Point")); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new NoteModel::EditCommand(m_model, - tr("Draw Point")); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point")); + m_editingCommand->add(m_editingPoint); m_editing = true; } @@ -927,7 +866,7 @@ double newValue = getValueForY(v, e->y()); - sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newFrame = m_editingPoint.getFrame(); sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; @@ -936,11 +875,12 @@ newDuration = 1; } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = newFrame; - m_editingPoint.value = float(newValue); - m_editingPoint.duration = newDuration; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(newFrame) + .withValue(float(newValue)) + .withDuration(newDuration); + m_editingCommand->add(m_editingPoint); } void @@ -980,13 +920,14 @@ m_editing = false; - NoteModel::Point p(0); + Event p(0); if (!getPointToDrag(v, e->x(), e->y(), p)) return; - if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; + if (p.getFrame() != m_editingPoint.getFrame() || + p.getValue() != m_editingPoint.getValue()) return; - m_editingCommand = new NoteModel::EditCommand(m_model, tr("Erase Point")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); - m_editingCommand->deletePoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; @@ -1003,8 +944,8 @@ 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); + m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); + m_dragPointY = getYForValue(v, m_editingPoint.getValue()); if (m_editingCommand) { finish(m_editingCommand); @@ -1035,14 +976,15 @@ double value = getValueForY(v, newy); if (!m_editingCommand) { - m_editingCommand = new NoteModel::EditCommand(m_model, + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.value = float(value); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(value)); + m_editingCommand->add(m_editingPoint); } void @@ -1055,8 +997,8 @@ QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { + if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { + if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Edit Point"); } else { newName = tr("Relocate Point"); @@ -1078,10 +1020,10 @@ { if (!m_model) return false; - NoteModel::Point note(0); + Event note(0); if (!getPointToDrag(v, e->x(), e->y(), note)) return false; -// NoteModel::Point note = *points.begin(); +// Event note = *points.begin(); ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), @@ -1091,26 +1033,26 @@ ItemEditDialog::ShowText, getScaleUnits()); - dialog->setFrameTime(note.frame); - dialog->setValue(note.value); - dialog->setFrameDuration(note.duration); - dialog->setText(note.label); + dialog->setFrameTime(note.getFrame()); + dialog->setValue(note.getValue()); + dialog->setFrameDuration(note.getDuration()); + dialog->setText(note.getLabel()); m_editingPoint = note; m_editIsOpen = true; if (dialog->exec() == QDialog::Accepted) { - NoteModel::Point newNote = note; - newNote.frame = dialog->getFrameTime(); - newNote.value = dialog->getValue(); - newNote.duration = dialog->getFrameDuration(); - newNote.label = dialog->getText(); + Event newNote = note + .withFrame(dialog->getFrameTime()) + .withValue(dialog->getValue()) + .withDuration(dialog->getFrameDuration()) + .withLabel(dialog->getText()); - NoteModel::EditCommand *command = new NoteModel::EditCommand + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Point")); - command->deletePoint(note); - command->addPoint(newNote); + command->remove(note); + command->add(newNote); finish(command); } @@ -1126,21 +1068,17 @@ { if (!m_model) return; - NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - NoteModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + for (Event p: points) { + command->remove(p); + Event moved = p.withFrame(p.getFrame() + + newStartFrame - s.getStartFrame()); + command->add(moved); } finish(command); @@ -1149,37 +1087,28 @@ void NoteLayer::resizeSelection(Selection s, Selection newSize) { - if (!m_model) return; + if (!m_model || !s.getDuration()) return; - NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); + + for (Event p: points) { - for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; + double newDuration = double(p.getDuration()) * ratio; - if (s.contains(i->frame)) { - - double targetStart = double(i->frame); - targetStart = double(newSize.getStartFrame()) + - targetStart - double(s.getStartFrame()) * ratio; - - double targetEnd = double(i->frame + i->duration); - targetEnd = double(newSize.getStartFrame()) + - targetEnd - double(s.getStartFrame()) * ratio; - - NoteModel::Point newPoint(*i); - newPoint.frame = lrint(targetStart); - newPoint.duration = lrint(targetEnd - targetStart); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)) + .withDuration(lrint(newDuration)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -1190,18 +1119,14 @@ { if (!m_model) return; - NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Delete Selected Points")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selected Points")); - NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - command->deletePoint(*i); - } + for (Event p: points) { + command->remove(p); } finish(command); @@ -1212,25 +1137,21 @@ { if (!m_model) return; - NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (NoteModel::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); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } bool -NoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) +NoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, + sv_frame_t /* frameOffset */, bool /* interactive */) { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + const EventVector &points = from.getPoints(); bool realign = false; @@ -1251,13 +1172,12 @@ } } - NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - - if (!i->haveFrame()) continue; + sv_frame_t frame = 0; if (!realign) { @@ -1266,7 +1186,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -1274,32 +1194,29 @@ } } - NoteModel::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 { + Event p = *i; + Event newPoint = p; + if (!p.hasValue()) { + newPoint = newPoint.withValue((m_model->getValueMinimum() + + m_model->getValueMaximum()) / 2); + } + if (!p.hasDuration()) { sv_frame_t nextFrame = frame; - Clipboard::PointList::const_iterator j = i; + EventVector::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(); + newPoint = newPoint.withDuration(m_model->getResolution()); } else { - newPoint.duration = nextFrame - frame; + newPoint = newPoint.withDuration(nextFrame - frame); } } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); @@ -1309,7 +1226,8 @@ void NoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) { - m_pendingNoteOns.insert(Note(frame, float(pitch), 0, float(velocity) / 127.f, "")); + m_pendingNoteOns.insert(Event(frame, float(pitch), 0, + float(velocity) / 127.f, QString())); } void @@ -1317,13 +1235,16 @@ { for (NoteSet::iterator i = m_pendingNoteOns.begin(); i != m_pendingNoteOns.end(); ++i) { - if (lrintf((*i).value) == pitch) { - Note note(*i); + + Event p = *i; + + if (lrintf(p.getValue()) == pitch) { m_pendingNoteOns.erase(i); - note.duration = frame - note.frame; + Event note = p.withDuration(frame - p.getFrame()); if (m_model) { - NoteModel::AddPointCommand *c = new NoteModel::AddPointCommand - (m_model, note, tr("Record Note")); + ChangeEventsCommand *c = new ChangeEventsCommand + (m_model, tr("Record Note")); + c->add(note); // execute and bundle: CommandHistory::getInstance()->addCommand(c, true, true); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/NoteLayer.h --- a/layer/NoteLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/NoteLayer.h Fri May 17 10:02:52 2019 +0100 @@ -142,9 +142,9 @@ int getDefaultColourHint(bool dark, bool &impose) override; - NoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int) const; - bool getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const; NoteModel *m_model; bool m_editing; @@ -152,13 +152,13 @@ int m_dragPointY; int m_dragStartX; int m_dragStartY; - NoteModel::Point m_originalPoint; - NoteModel::Point m_editingPoint; - NoteModel::EditCommand *m_editingCommand; + Event m_originalPoint; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; bool m_editIsOpen; VerticalScale m_verticalScale; - typedef std::set NoteSet; + typedef std::set NoteSet; NoteSet m_pendingNoteOns; mutable double m_scaleMinimum; @@ -166,7 +166,7 @@ bool shouldAutoAlign() const; - void finish(NoteModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/RegionLayer.cpp --- a/layer/RegionLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/RegionLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -261,10 +261,10 @@ // SVDEBUG << "RegionLayer::recalcSpacing" << endl; - for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin(); - i != m_model->getPoints().end(); ++i) { - m_distributionMap[i->value]++; -// SVDEBUG << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << endl; + EventVector allEvents = m_model->getAllEvents(); + for (const Event &e: allEvents) { + m_distributionMap[e.getValue()]++; +// SVDEBUG << "RegionLayer::recalcSpacing: value found: " << e.getValue() << " (now have " << m_distributionMap[e.getValue()] << " of this value)" << endl; } int n = 0; @@ -303,69 +303,46 @@ return true; } -RegionModel::PointList +EventVector RegionLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { - if (!m_model) return RegionModel::PointList(); + if (!m_model) return EventVector(); sv_frame_t frame = v->getFrameForX(x); - RegionModel::PointList onPoints = - m_model->getPoints(frame); + EventVector local = m_model->getEventsCovering(frame); + if (!local.empty()) return local; - if (!onPoints.empty()) { - return onPoints; - } + int fuzz = ViewManager::scalePixelSize(2); + sv_frame_t start = v->getFrameForX(x - fuzz); + sv_frame_t end = v->getFrameForX(x + fuzz); - RegionModel::PointList prevPoints = - m_model->getPreviousPoints(frame); - RegionModel::PointList nextPoints = - m_model->getNextPoints(frame); + local = m_model->getEventsStartingWithin(frame, end - frame); + if (!local.empty()) return local; - RegionModel::PointList usePoints = prevPoints; + local = m_model->getEventsSpanning(start, frame - start); + if (!local.empty()) return local; - 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 = ViewManager::scalePixelSize(2); - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } - } - - return usePoints; + return {}; } bool -RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &p) const +RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const { if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); - RegionModel::PointList onPoints = m_model->getPoints(frame); + EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return false; int nearestDistance = -1; - - for (RegionModel::PointList::const_iterator i = onPoints.begin(); - i != onPoints.end(); ++i) { - - int distance = getYForValue(v, (*i).value) - y; + for (const auto &p: onPoints) { + int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; - p = *i; + point = p; } } @@ -376,12 +353,16 @@ RegionLayer::getLabelPreceding(sv_frame_t frame) const { if (!m_model) return ""; - RegionModel::PointList points = m_model->getPreviousPoints(frame); - for (RegionModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - if (i->label != "") return i->label; + EventVector points = m_model->getEventsStartingWithin + (m_model->getStartFrame(), frame - m_model->getStartFrame()); + if (!points.empty()) { + for (auto i = points.rbegin(); i != points.rend(); ++i) { + if (i->getLabel() != QString()) { + return i->getLabel(); + } + } } - return ""; + return QString(); } QString @@ -391,7 +372,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - RegionModel::PointList points = getLocalPoints(v, x); + EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { @@ -401,19 +382,20 @@ } } - RegionRec region(0); - RegionModel::PointList::iterator i; + Event region; + EventVector::iterator i; //!!! harmonise with whatever decision is made about point y //!!! coords in paint method for (i = points.begin(); i != points.end(); ++i) { - int y = getYForValue(v, i->value); + int y = getYForValue(v, i->getValue()); int h = 3; if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + h = y - getYForValue + (v, i->getValue() + m_model->getValueQuantization()); if (h < 3) h = 3; } @@ -425,18 +407,18 @@ if (i == points.end()) return tr("No local points"); - RealTime rt = RealTime::frame2RealTime(region.frame, + RealTime rt = RealTime::frame2RealTime(region.getFrame(), m_model->getSampleRate()); - RealTime rd = RealTime::frame2RealTime(region.duration, + RealTime rd = RealTime::frame2RealTime(region.getDuration(), m_model->getSampleRate()); QString valueText; - valueText = tr("%1 %2").arg(region.value).arg(getScaleUnits()); + valueText = tr("%1 %2").arg(region.getValue()).arg(getScaleUnits()); QString text; - if (region.label == "") { + if (region.getLabel() == "") { text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) .arg(rt.toText(true).c_str()) .arg(valueText) @@ -446,11 +428,11 @@ .arg(rt.toText(true).c_str()) .arg(valueText) .arg(rd.toText(true).c_str()) - .arg(region.label); + .arg(region.getLabel()); } - pos = QPoint(v->getXForFrame(region.frame), - getYForValue(v, region.value)); + pos = QPoint(v->getXForFrame(region.getFrame()), + getYForValue(v, region.getValue())); return text; } @@ -463,78 +445,69 @@ return Layer::snapToFeatureFrame(v, frame, resolution, snap); } + // SnapLeft / SnapRight: return frame of nearest feature in that + // direction no matter how far away + // + // SnapNeighbouring: return frame of feature that would be used in + // an editing operation, i.e. closest feature in either direction + // but only if it is "close enough" + resolution = m_model->getResolution(); - RegionModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); + EventVector points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; } - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; + // Normally we snap to the start frame of whichever event we + // find. However here, for SnapRight only, if the end frame of + // whichever event we would have snapped to had we been snapping + // left is closer than the start frame of the next event to the + // right, then we snap to that frame instead. Clear? + + Event left; + bool haveLeft = false; + if (m_model->getNearestEventMatching + (frame, [](Event) { return true; }, EventSeries::Backward, left)) { + haveLeft = true; + } - for (RegionModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + if (snap == SnapLeft) { + frame = left.getFrame(); + return haveLeft; + } - if (snap == SnapRight) { + Event right; + bool haveRight = false; + if (m_model->getNearestEventMatching + (frame, [](Event) { return true; }, EventSeries::Forward, right)) { + haveRight = true; + } - // The best frame to snap to is the end frame of whichever - // feature we would have snapped to the start frame of if - // we had been snapping left. - - if (i->frame <= frame) { - if (i->frame + i->duration > frame) { - snapped = i->frame + i->duration; - found = true; // don't break, as the next may be better + if (haveLeft) { + sv_frame_t leftEnd = left.getFrame() + left.getDuration(); + if (leftEnd > frame) { + if (haveRight) { + if (leftEnd - frame < right.getFrame() - frame) { + frame = leftEnd; + } else { + frame = right.getFrame(); } } else { - if (!found) { - snapped = i->frame; - found = true; - } - break; + frame = leftEnd; } - - } 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 - - RegionModel::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; - } + return true; } } - frame = snapped; - return found; + if (haveRight) { + frame = right.getFrame(); + return true; + } + + return false; } bool @@ -546,76 +519,41 @@ return Layer::snapToSimilarFeature(v, frame, resolution, snap); } + // snap is only permitted to be SnapLeft or SnapRight here. We + // don't do the same trick as in snapToFeatureFrame, of snapping + // to the end of a feature sometimes. + resolution = m_model->getResolution(); - const RegionModel::PointList &points = m_model->getPoints(); - RegionModel::PointList close = m_model->getPoints(frame, frame); + Event ref; + Event e; + float matchvalue; + bool found; - RegionModel::PointList::const_iterator i; + found = m_model->getNearestEventMatching + (frame, [](Event) { return true; }, EventSeries::Backward, ref); - sv_frame_t matchframe = frame; - double matchvalue = 0.f; - - for (i = close.begin(); i != close.end(); ++i) { - if (i->frame > frame) break; - matchvalue = i->value; - matchframe = i->frame; + if (!found) { + return false; } - sv_frame_t snapped = frame; - bool found = false; - bool distant = false; - double epsilon = 0.0001; + matchvalue = ref.getValue(); + + found = m_model->getNearestEventMatching + (frame, + [matchvalue](Event e) { + double epsilon = 0.0001; + return fabs(e.getValue() - matchvalue) < epsilon; + }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e); - i = close.begin(); - - // Scan through the close points first, then the more distant ones - // if no suitable close one is found. So the while-termination - // condition here can only happen once i has passed through the - // whole of the close container and then the whole of the separate - // points container. The two iterators are totally distinct, but - // have the same type so we cheekily use the same variable and a - // single loop for both. - - while (i != points.end()) { - - if (!distant) { - if (i == close.end()) { - // switch from the close container to the points container - i = points.begin(); - distant = true; - } - } - - if (snap == SnapRight) { - - if (i->frame > matchframe && - fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame < matchframe) { - if (fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } - } else if (found || distant) { - break; - } - - } else { - // no other snap types supported - } - - ++i; + if (!found) { + return false; } - frame = snapped; - return found; + frame = e.getFrame(); + return true; } QString @@ -882,7 +820,8 @@ sv_frame_t wholeFrame0 = v->getFrameForX(0); sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth()); - RegionModel::PointList points(m_model->getPoints(wholeFrame0, wholeFrame1)); + EventVector points(m_model->getEventsSpanning(wholeFrame0, + wholeFrame1 - wholeFrame0)); if (points.empty()) return; paint.setPen(getBaseQColor()); @@ -898,7 +837,7 @@ if (max == min) max = min + 1.0; QPoint localPos; - RegionModel::Point illuminatePoint(0); + Event illuminatePoint(0); bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { @@ -917,30 +856,31 @@ int fontHeight = paint.fontMetrics().height(); - for (RegionModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const RegionModel::Point &p(*i); + const Event &p(*i); - int x = v->getXForFrame(p.frame); - int w = v->getXForFrame(p.frame + p.duration) - x; - int y = getYForValue(v, p.value); + int x = v->getXForFrame(p.getFrame()); + int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; + int y = getYForValue(v, p.getValue()); int h = 9; int ex = x + w; int gap = v->scalePixelSize(2); - RegionModel::PointList::const_iterator j = i; + EventVector::const_iterator j = i; ++j; if (j != points.end()) { - const RegionModel::Point &q(*j); - int nx = v->getXForFrame(q.frame); + const Event &q(*j); + int nx = v->getXForFrame(q.getFrame()); if (nx < ex) ex = nx; } if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + h = y - getYForValue + (v, p.getValue() + m_model->getValueQuantization()); if (h < 3) h = 3; } @@ -948,7 +888,7 @@ if (m_plotStyle == PlotSegmentation) { paint.setPen(getForegroundQColor(v->getView())); - paint.setBrush(getColourForValue(v, p.value)); + paint.setBrush(getColourForValue(v, p.getValue())); } else { paint.setPen(getBaseQColor()); paint.setBrush(brushColour); @@ -958,10 +898,7 @@ if (ex <= x) continue; - if (!shouldIlluminate || - // "illuminatePoint != p" - RegionModel::Point::Comparator()(illuminatePoint, p) || - RegionModel::Point::Comparator()(p, illuminatePoint)) { + if (!shouldIlluminate || illuminatePoint != p) { paint.setPen(QPen(getForegroundQColor(v->getView()), 1)); paint.drawLine(x, 0, x, v->getPaintHeight()); @@ -975,15 +912,13 @@ } else { - if (shouldIlluminate && - // "illuminatePoint == p" - !RegionModel::Point::Comparator()(illuminatePoint, p) && - !RegionModel::Point::Comparator()(p, illuminatePoint)) { + if (shouldIlluminate && illuminatePoint == p) { paint.setPen(v->getForeground()); paint.setBrush(v->getForeground()); - QString vlabel = QString("%1%2").arg(p.value).arg(getScaleUnits()); + QString vlabel = + QString("%1%2").arg(p.getValue()).arg(getScaleUnits()); PaintAssistant::drawVisibleText(v, paint, x - paint.fontMetrics().width(vlabel) - gap, y + paint.fontMetrics().height()/2 @@ -991,7 +926,7 @@ vlabel, PaintAssistant::OutlinedText); QString hlabel = RealTime::frame2RealTime - (p.frame, m_model->getSampleRate()).toText(true).c_str(); + (p.getFrame(), m_model->getSampleRate()).toText(true).c_str(); PaintAssistant::drawVisibleText(v, paint, x, y - h/2 - paint.fontMetrics().descent() - gap, @@ -1008,18 +943,18 @@ int nextLabelMinX = -100; int lastLabelY = 0; - for (RegionModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const RegionModel::Point &p(*i); + const Event &p(*i); - int x = v->getXForFrame(p.frame); - int w = v->getXForFrame(p.frame + p.duration) - x; - int y = getYForValue(v, p.value); + int x = v->getXForFrame(p.getFrame()); + int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; + int y = getYForValue(v, p.getValue()); - QString label = p.label; + QString label = p.getLabel(); if (label == "") { - label = QString("%1%2").arg(p.value).arg(getScaleUnits()); + label = QString("%1%2").arg(p.getValue()).arg(getScaleUnits()); } int labelWidth = paint.fontMetrics().width(label); @@ -1038,12 +973,7 @@ bool illuminated = false; if (m_plotStyle != PlotSegmentation) { - - if (shouldIlluminate && - // "illuminatePoint == p" - !RegionModel::Point::Comparator()(illuminatePoint, p) && - !RegionModel::Point::Comparator()(p, illuminatePoint)) { - + if (shouldIlluminate && illuminatePoint == p) { illuminated = true; } } @@ -1101,7 +1031,7 @@ void RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model || m_model->getPoints().empty()) return; + if (!m_model || m_model->isEmpty()) return; QString unit; double min, max; @@ -1152,13 +1082,13 @@ double value = getValueForY(v, e->y()); - m_editingPoint = RegionModel::Point(frame, float(value), 0, ""); + m_editingPoint = Event(frame, float(value), 0, ""); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new RegionModel::EditCommand(m_model, + m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Region")); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->add(m_editingPoint); recalcSpacing(); @@ -1174,10 +1104,10 @@ if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - double newValue = m_editingPoint.value; + double newValue = m_editingPoint.getValue(); if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y()); - sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newFrame = m_editingPoint.getFrame(); sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; @@ -1186,11 +1116,12 @@ newDuration = 1; } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = newFrame; - m_editingPoint.value = float(newValue); - m_editingPoint.duration = newDuration; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(newFrame) + .withValue(float(newValue)) + .withDuration(newDuration); + m_editingCommand->add(m_editingPoint); recalcSpacing(); } @@ -1234,14 +1165,15 @@ m_editing = false; - RegionModel::Point p(0); + Event p(0); if (!getPointToDrag(v, e->x(), e->y(), p)) return; - if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; + if (p.getFrame() != m_editingPoint.getFrame() || + p.getValue() != m_editingPoint.getValue()) return; - m_editingCommand = new RegionModel::EditCommand + m_editingCommand = new ChangeEventsCommand (m_model, tr("Erase Region")); - m_editingCommand->deletePoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; @@ -1258,8 +1190,8 @@ return; } - m_dragPointX = v->getXForFrame(m_editingPoint.frame); - m_dragPointY = getYForValue(v, m_editingPoint.value); + m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); + m_dragPointY = getYForValue(v, m_editingPoint.getValue()); m_originalPoint = m_editingPoint; @@ -1290,22 +1222,23 @@ // Do not bisect between two values, if one of those values is // that of the point we're actually moving ... - int avoid = m_spacingMap[m_editingPoint.value]; + int avoid = m_spacingMap[m_editingPoint.getValue()]; // ... unless there are other points with the same value - if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1; + if (m_distributionMap[m_editingPoint.getValue()] > 1) avoid = -1; double value = getValueForY(v, newy, avoid); if (!m_editingCommand) { - m_editingCommand = new RegionModel::EditCommand(m_model, + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Region")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.value = float(value); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(value)); + m_editingCommand->add(m_editingPoint); recalcSpacing(); } @@ -1318,8 +1251,8 @@ QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { + if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { + if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Edit Region"); } else { newName = tr("Relocate Region"); @@ -1342,7 +1275,7 @@ { if (!m_model) return false; - RegionModel::Point region(0); + Event region(0); if (!getPointToDrag(v, e->x(), e->y(), region)) return false; ItemEditDialog *dialog = new ItemEditDialog @@ -1353,23 +1286,23 @@ ItemEditDialog::ShowText, getScaleUnits()); - dialog->setFrameTime(region.frame); - dialog->setValue(region.value); - dialog->setFrameDuration(region.duration); - dialog->setText(region.label); + dialog->setFrameTime(region.getFrame()); + dialog->setValue(region.getValue()); + dialog->setFrameDuration(region.getDuration()); + dialog->setText(region.getLabel()); if (dialog->exec() == QDialog::Accepted) { - RegionModel::Point newRegion = region; - newRegion.frame = dialog->getFrameTime(); - newRegion.value = dialog->getValue(); - newRegion.duration = dialog->getFrameDuration(); - newRegion.label = dialog->getText(); + Event newRegion = region + .withFrame(dialog->getFrameTime()) + .withValue(dialog->getValue()) + .withDuration(dialog->getFrameDuration()) + .withLabel(dialog->getText()); - RegionModel::EditCommand *command = new RegionModel::EditCommand + ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Region")); - command->deletePoint(region); - command->addPoint(newRegion); + command->remove(region); + command->add(newRegion); finish(command); } @@ -1383,21 +1316,19 @@ { if (!m_model) return; - RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (RegionModel::PointList::iterator i = points.begin(); + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - if (s.contains(i->frame)) { - RegionModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = (*i) + .withFrame(i->getFrame() + newStartFrame - s.getStartFrame()); + command->remove(*i); + command->add(newPoint); } finish(command); @@ -1407,37 +1338,28 @@ void RegionLayer::resizeSelection(Selection s, Selection newSize) { - if (!m_model) return; + if (!m_model || !s.getDuration()) return; - RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); + + for (Event p: points) { - for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; + double newDuration = double(p.getDuration()) * ratio; - if (s.contains(i->frame)) { - - double targetStart = double(i->frame); - targetStart = double(newSize.getStartFrame()) + - targetStart - double(s.getStartFrame()) * ratio; - - double targetEnd = double(i->frame + i->duration); - targetEnd = double(newSize.getStartFrame()) + - targetEnd - double(s.getStartFrame()) * ratio; - - RegionModel::Point newPoint(*i); - newPoint.frame = lrint(targetStart); - newPoint.duration = lrint(targetEnd - targetStart); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)) + .withDuration(lrint(newDuration)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -1449,17 +1371,17 @@ { if (!m_model) return; - RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selected Points")); - RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (RegionModel::PointList::iterator i = points.begin(); + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - if (s.contains(i->frame)) { - command->deletePoint(*i); + if (s.contains(i->getFrame())) { + command->remove(*i); } } @@ -1472,16 +1394,11 @@ { if (!m_model) return; - RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->value, i->duration, i->label); - point.setReferenceFrame(alignToReference(v, i->frame)); - to.addPoint(point); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -1490,7 +1407,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + const EventVector &points = from.getPoints(); bool realign = false; @@ -1511,13 +1428,12 @@ } } - RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; sv_frame_t frame = 0; if (!realign) { @@ -1526,7 +1442,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -1534,31 +1450,29 @@ } } - RegionModel::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->haveDuration()) newPoint.duration = i->getDuration(); - else { + Event p = *i; + Event newPoint = p; + if (!p.hasValue()) { + newPoint = newPoint.withValue((m_model->getValueMinimum() + + m_model->getValueMaximum()) / 2); + } + if (!p.hasDuration()) { sv_frame_t nextFrame = frame; - Clipboard::PointList::const_iterator j = i; + EventVector::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(); + newPoint = newPoint.withDuration(m_model->getResolution()); } else { - newPoint.duration = nextFrame - frame; + newPoint = newPoint.withDuration(nextFrame - frame); } } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); diff -r 8d5bf4ab98ef -r 42c87368287c layer/RegionLayer.h --- a/layer/RegionLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/RegionLayer.h Fri May 17 10:02:52 2019 +0100 @@ -48,11 +48,11 @@ QString getLabelPreceding(sv_frame_t) const override; bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const override; + int &resolution, + SnapType snap) const override; bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const override; + int &resolution, + SnapType snap) const override; void drawStart(LayerGeometryProvider *v, QMouseEvent *) override; void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override; @@ -141,9 +141,9 @@ int getDefaultColourHint(bool dark, bool &impose) override; - RegionModel::PointList getLocalPoints(LayerGeometryProvider *v, int x) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int x) const; - bool getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const; RegionModel *m_model; bool m_editing; @@ -151,9 +151,9 @@ int m_dragPointY; int m_dragStartX; int m_dragStartY; - RegionModel::Point m_originalPoint; - RegionModel::Point m_editingPoint; - RegionModel::EditCommand *m_editingCommand; + Event m_originalPoint; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; VerticalScale m_verticalScale; int m_colourMap; bool m_colourInverted; @@ -170,7 +170,7 @@ int spacingIndexToY(LayerGeometryProvider *v, int i) const; double yToSpacingIndex(LayerGeometryProvider *v, int y) const; - void finish(RegionModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/SpectrogramLayer.cpp --- a/layer/SpectrogramLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/SpectrogramLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -1411,7 +1411,10 @@ FFTModel *oldModel = m_fftModel; m_fftModel = newModel; - if (canStoreWholeCache()) { // i.e. if enough memory + bool createWholeCache = false; + checkCacheSpace(&m_peakCacheDivisor, &createWholeCache); + + if (createWholeCache) { m_wholeCache = new Dense3DModelPeakCache(m_fftModel, 1); m_peakCache = new Dense3DModelPeakCache(m_wholeCache, m_peakCacheDivisor); } else { @@ -1422,12 +1425,14 @@ delete oldModel; } -bool -SpectrogramLayer::canStoreWholeCache() const +void +SpectrogramLayer::checkCacheSpace(int *suggestedPeakDivisor, + bool *createWholeCache) const { - if (!m_fftModel) { - return false; // or true, doesn't really matter - } + *suggestedPeakDivisor = 8; + *createWholeCache = false; + + if (!m_fftModel) return; size_t sz = size_t(m_fftModel->getWidth()) * @@ -1436,23 +1441,28 @@ try { SVDEBUG << "Requesting advice from StorageAdviser on whether to create whole-model cache" << endl; + // The lower amount here is the amount required for the + // slightly higher-resolution version of the peak cache + // without a whole-model cache; the higher amount is that for + // the whole-model cache. The factors of 1024 are because + // StorageAdviser rather stupidly works in kilobytes StorageAdviser::Recommendation recommendation = StorageAdviser::recommend (StorageAdviser::Criteria(StorageAdviser::SpeedCritical | StorageAdviser::PrecisionCritical | StorageAdviser::FrequentLookupLikely), - sz / 1024, sz / 1024); - if ((recommendation & StorageAdviser::UseDisc) || - (recommendation & StorageAdviser::ConserveSpace)) { + (sz / 8) / 1024, sz / 1024); + if (recommendation & StorageAdviser::UseDisc) { SVDEBUG << "Seems inadvisable to create whole-model cache" << endl; - return false; - } else { + } else if (recommendation & StorageAdviser::ConserveSpace) { + SVDEBUG << "Seems inadvisable to create whole-model cache but acceptable to use the slightly higher-resolution peak cache" << endl; + *suggestedPeakDivisor = 4; + } else { SVDEBUG << "Seems fine to create whole-model cache" << endl; - return true; + *createWholeCache = true; } } catch (const InsufficientDiscSpace &) { SVDEBUG << "Seems like a terrible idea to create whole-model cache" << endl; - return false; } } @@ -1792,7 +1802,6 @@ switch (snap) { case SnapLeft: frame = left; break; case SnapRight: frame = right; break; - case SnapNearest: case SnapNeighbouring: if (frame - left > right - frame) frame = right; else frame = left; @@ -1861,7 +1870,7 @@ QPoint cursorPos) const { paint.save(); - + int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); QFont fn = paint.font(); diff -r 8d5bf4ab98ef -r 42c87368287c layer/SpectrogramLayer.h --- a/layer/SpectrogramLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/SpectrogramLayer.h Fri May 17 10:02:52 2019 +0100 @@ -311,8 +311,9 @@ Dense3DModelPeakCache *m_wholeCache; Dense3DModelPeakCache *m_peakCache; Dense3DModelPeakCache *getPeakCache() const { return m_peakCache; } - const int m_peakCacheDivisor; - bool canStoreWholeCache() const; + int m_peakCacheDivisor; + void checkCacheSpace(int *suggestedPeakDivisor, + bool *createWholeCache) const; void recreateFFTModel(); typedef std::map ViewMagMap; // key is view id diff -r 8d5bf4ab98ef -r 42c87368287c layer/TextLayer.cpp --- a/layer/TextLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TextLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -108,29 +108,29 @@ return !v->shouldIlluminateLocalFeatures(this, discard); } - -TextModel::PointList +EventVector TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const { - if (!m_model) return TextModel::PointList(); + if (!m_model) return {}; - sv_frame_t frame0 = v->getFrameForX(-150); - sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150); + int overlap = ViewManager::scalePixelSize(150); - TextModel::PointList points(m_model->getPoints(frame0, frame1)); + sv_frame_t frame0 = v->getFrameForX(-overlap); + sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + overlap); + + EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0)); - TextModel::PointList rv; + EventVector rv; QFontMetrics metrics = QFontMetrics(QFont()); - for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - const TextModel::Point &p(*i); + Event p(*i); - int px = v->getXForFrame(p.frame); - int py = getYForHeight(v, p.height); + int px = v->getXForFrame(p.getFrame()); + int py = getYForHeight(v, p.getValue()); - QString label = p.label; + QString label = p.getLabel(); if (label == "") { label = tr(""); } @@ -146,7 +146,7 @@ if (x >= px && x < px + rect.width() && y >= py && y < py + rect.height()) { - rv.insert(p); + rv.push_back(p); } } @@ -154,22 +154,22 @@ } bool -TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const +TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &p) const { if (!m_model) return false; - sv_frame_t a = v->getFrameForX(x - 120); - sv_frame_t b = v->getFrameForX(x + 10); - TextModel::PointList onPoints = m_model->getPoints(a, b); + sv_frame_t a = v->getFrameForX(x - ViewManager::scalePixelSize(120)); + sv_frame_t b = v->getFrameForX(x + ViewManager::scalePixelSize(10)); + EventVector onPoints = m_model->getEventsWithin(a, b); if (onPoints.empty()) return false; double nearestDistance = -1; - for (TextModel::PointList::const_iterator i = onPoints.begin(); + for (EventVector::const_iterator i = onPoints.begin(); i != onPoints.end(); ++i) { - double yd = getYForHeight(v, (*i).height) - y; - double xd = v->getXForFrame((*i).frame) - x; + double yd = getYForHeight(v, i->getValue()) - y; + double xd = v->getXForFrame(i->getFrame()) - x; double distance = sqrt(yd*yd + xd*xd); if (nearestDistance == -1 || distance < nearestDistance) { @@ -188,7 +188,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - TextModel::PointList points = getLocalPoints(v, x, pos.y()); + EventVector points = getLocalPoints(v, x, pos.y()); if (points.empty()) { if (!m_model->isReady()) { @@ -198,21 +198,21 @@ } } - sv_frame_t useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->getFrame(); RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); QString text; - if (points.begin()->label == "") { + if (points.begin()->getLabel() == "") { text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) .arg(rt.toText(true).c_str()) - .arg(points.begin()->height) - .arg(points.begin()->label); + .arg(points.begin()->getValue()) + .arg(points.begin()->getLabel()); } pos = QPoint(v->getXForFrame(useFrame), - getYForHeight(v, points.begin()->height)); + getYForHeight(v, points.begin()->getValue())); return text; } @@ -228,67 +228,33 @@ return Layer::snapToFeatureFrame(v, frame, resolution, snap); } + // SnapLeft / SnapRight: return frame of nearest feature in that + // direction no matter how far away + // + // SnapNeighbouring: return frame of feature that would be used in + // an editing operation, i.e. closest feature in either direction + // but only if it is "close enough" + resolution = m_model->getResolution(); - TextModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame), -1); + EventVector points = getLocalPoints(v, v->getXForFrame(frame), -1); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; } - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; - - for (TextModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - - if (snap == SnapRight) { - - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } - - } else { // nearest - - TextModel::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; - } - } + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event) { return true; }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e)) { + frame = e.getFrame(); + return true; } - frame = snapped; - return found; + return false; } int @@ -316,10 +282,11 @@ // Profiler profiler("TextLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); - sv_frame_t frame0 = v->getFrameForX(x0); - sv_frame_t frame1 = v->getFrameForX(x1); + int overlap = ViewManager::scalePixelSize(150); + sv_frame_t frame0 = v->getFrameForX(x0 - overlap); + sv_frame_t frame1 = v->getFrameForX(x1 + overlap); - TextModel::PointList points(m_model->getPoints(frame0, frame1)); + EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 2)); if (points.empty()) return; QColor brushColour(getBaseQColor()); @@ -335,7 +302,7 @@ // << m_model->getResolution() << " frames" << endl; QPoint localPos; - TextModel::Point illuminatePoint(0); + Event illuminatePoint(0); bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { @@ -349,18 +316,15 @@ paint.save(); paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight()); - for (TextModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const TextModel::Point &p(*i); + Event p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForHeight(v, p.height); + int x = v->getXForFrame(p.getFrame()); + int y = getYForHeight(v, p.getValue()); - if (!shouldIlluminate || - // "illuminatePoint != p" - TextModel::Point::Comparator()(illuminatePoint, p) || - TextModel::Point::Comparator()(p, illuminatePoint)) { + if (!shouldIlluminate || illuminatePoint != p) { paint.setPen(penColour); paint.setBrush(brushColour); } else { @@ -368,7 +332,7 @@ paint.setPen(v->getBackground()); } - QString label = p.label; + QString label = p.getLabel(); if (label == "") { label = tr(""); } @@ -399,8 +363,8 @@ Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); -/// if (p.label != "") { -/// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); +/// if (p.getLabel() != "") { +/// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.getLabel()); /// } } @@ -426,12 +390,12 @@ double height = getHeightForY(v, e->y()); - m_editingPoint = TextModel::Point(frame, float(height), ""); + m_editingPoint = Event(frame, float(height), ""); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new TextModel::EditCommand(m_model, "Add Label"); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand = new ChangeEventsCommand(m_model, "Add Label"); + m_editingCommand->add(m_editingPoint); m_editing = true; } @@ -449,10 +413,11 @@ double height = getHeightForY(v, e->y()); - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.height = float(height); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(height)); + m_editingCommand->add(m_editingPoint); } void @@ -466,12 +431,12 @@ tr("Please enter a new label:"), QLineEdit::Normal, "", &ok); + m_editingCommand->remove(m_editingPoint); + if (ok) { - TextModel::RelabelCommand *command = - new TextModel::RelabelCommand(m_model, m_editingPoint, label); - m_editingCommand->addCommand(command); - } else { - m_editingCommand->deletePoint(m_editingPoint); + m_editingPoint = m_editingPoint + .withLabel(label); + m_editingCommand->add(m_editingPoint); } finish(m_editingCommand); @@ -506,15 +471,13 @@ m_editing = false; - TextModel::Point p(0); + Event p; if (!getPointToDrag(v, e->x(), e->y(), p)) return; - if (p.frame != m_editingPoint.frame || p.height != m_editingPoint.height) return; + if (p.getFrame() != m_editingPoint.getFrame() || + p.getValue() != m_editingPoint.getValue()) return; - m_editingCommand = new TextModel::EditCommand - (m_model, tr("Erase Point")); - - m_editingCommand->deletePoint(m_editingPoint); - + m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; @@ -547,26 +510,26 @@ { if (!m_model || !m_editing) return; - sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); - double heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); + sv_frame_t frameDiff = + v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); + double heightDiff = + getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); - sv_frame_t frame = m_originalPoint.frame + frameDiff; - double height = m_originalPoint.height + heightDiff; + sv_frame_t frame = m_originalPoint.getFrame() + frameDiff; + double height = m_originalPoint.getValue() + heightDiff; -// sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = (frame / m_model->getResolution()) * m_model->getResolution(); -// double height = getHeightForY(v, e->y()); - if (!m_editingCommand) { - m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Label")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.height = float(height); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(height)); + m_editingCommand->add(m_editingPoint); } void @@ -579,8 +542,8 @@ QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.height != m_originalPoint.height) { + if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { + if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Move Label"); } else { newName = tr("Move Label Horizontally"); @@ -602,19 +565,21 @@ { if (!m_model) return false; - TextModel::Point text(0); + Event text; if (!getPointToDrag(v, e->x(), e->y(), text)) return false; - QString label = text.label; + QString label = text.getLabel(); bool ok = false; label = QInputDialog::getText(v->getView(), tr("Enter label"), tr("Please enter a new label:"), QLineEdit::Normal, label, &ok); - if (ok && label != text.label) { - TextModel::RelabelCommand *command = - new TextModel::RelabelCommand(m_model, text, label); - CommandHistory::getInstance()->addCommand(command); + if (ok && label != text.getLabel()) { + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Re-Label Point")); + command->remove(text); + command->add(text.withLabel(label)); + finish(command); } return true; @@ -625,21 +590,17 @@ { if (!m_model) return; - TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - TextModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + for (Event p: points) { + command->remove(p); + Event moved = p.withFrame(p.getFrame() + + newStartFrame - s.getStartFrame()); + command->add(moved); } finish(command); @@ -650,30 +611,24 @@ { if (!m_model) return; - TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); + + for (Event p: points) { - for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; - if (s.contains(i->frame)) { - - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; - - TextModel::Point newPoint(*i); - newPoint.frame = lrint(target); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -684,15 +639,14 @@ { if (!m_model) return; - TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Delete Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selection")); - TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + for (Event p: points) { + command->remove(p); } finish(command); @@ -703,16 +657,11 @@ { if (!m_model) return; - TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); - for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->height, i->label); - point.setReferenceFrame(alignToReference(v, i->frame)); - to.addPoint(point); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -721,7 +670,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + const EventVector &points = from.getPoints(); bool realign = false; @@ -742,23 +691,22 @@ } } - TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); double valueMin = 0.0, valueMax = 1.0; - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (i->haveValue()) { + if (i->hasValue()) { if (i->getValue() < valueMin) valueMin = i->getValue(); if (i->getValue() > valueMax) valueMax = i->getValue(); } } if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0; - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; sv_frame_t frame = 0; if (!realign) { @@ -767,7 +715,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -775,23 +723,24 @@ } } - TextModel::Point newPoint(frame); - - if (i->haveValue()) { - newPoint.height = float((i->getValue() - valueMin) / (valueMax - valueMin)); + Event p = *i; + Event newPoint = p; + if (p.hasValue()) { + newPoint = newPoint.withValue(float((i->getValue() - valueMin) / + (valueMax - valueMin))); } else { - newPoint.height = 0.5f; + newPoint = newPoint.withValue(0.5f); } - if (i->haveLabel()) { - newPoint.label = i->getLabel(); - } else if (i->haveValue()) { - newPoint.label = QString("%1").arg(i->getValue()); - } else { - newPoint.label = tr("New Point"); + if (!p.hasLabel()) { + if (p.hasValue()) { + newPoint = newPoint.withLabel(QString("%1").arg(p.getValue())); + } else { + newPoint = newPoint.withLabel(tr("New Point")); + } } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); diff -r 8d5bf4ab98ef -r 42c87368287c layer/TextLayer.h --- a/layer/TextLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TextLayer.h Fri May 17 10:02:52 2019 +0100 @@ -96,18 +96,18 @@ int getDefaultColourHint(bool dark, bool &impose) override; - TextModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int x, int y) const; - bool getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const; TextModel *m_model; bool m_editing; QPoint m_editOrigin; - TextModel::Point m_originalPoint; - TextModel::Point m_editingPoint; - TextModel::EditCommand *m_editingCommand; + Event m_originalPoint; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; - void finish(TextModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/TimeInstantLayer.cpp --- a/layer/TimeInstantLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TimeInstantLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -155,62 +155,66 @@ return !v->shouldIlluminateLocalFeatures(this, discard); } -SparseOneDimensionalModel::PointList +EventVector TimeInstantLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { + if (!m_model) return {}; + // Return a set of points that all have the same frame number, the // nearest to the given x coordinate, and that are within a // certain fuzz distance of that x coordinate. - if (!m_model) return SparseOneDimensionalModel::PointList(); - sv_frame_t frame = v->getFrameForX(x); - SparseOneDimensionalModel::PointList onPoints = - m_model->getPoints(frame); + EventVector exact = m_model->getEventsStartingAt(frame); + if (!exact.empty()) return exact; - if (!onPoints.empty()) { - return onPoints; - } + // overspill == 1, so one event either side of the given span + EventVector neighbouring = m_model->getEventsWithin + (frame, m_model->getResolution(), 1); - SparseOneDimensionalModel::PointList prevPoints = - m_model->getPreviousPoints(frame); - SparseOneDimensionalModel::PointList nextPoints = - m_model->getNextPoints(frame); - - SparseOneDimensionalModel::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 (nextPoints.begin()->frame - frame < - frame - prevPoints.begin()->frame) { - usePoints = nextPoints; - } - - if (!usePoints.empty()) { - int fuzz = ViewManager::scalePixelSize(2); - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); + double fuzz = v->scaleSize(2); + sv_frame_t suitable = 0; + bool have = false; + + for (Event e: neighbouring) { + sv_frame_t f = e.getFrame(); + if (f < v->getStartFrame() || f > v->getEndFrame()) { + continue; + } + int px = v->getXForFrame(f); + if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) { + continue; + } + if (!have) { + suitable = f; + have = true; + } else if (llabs(frame - f) < llabs(suitable - f)) { + suitable = f; } } - return usePoints; + if (have) { + return m_model->getEventsStartingAt(suitable); + } else { + return {}; + } } QString TimeInstantLayer::getLabelPreceding(sv_frame_t frame) const { - if (!m_model) return ""; - SparseOneDimensionalModel::PointList points = m_model->getPreviousPoints(frame); - for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - if (i->label != "") return i->label; + if (!m_model || !m_model->hasTextLabels()) return ""; + + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event e) { return e.hasLabel() && e.getLabel() != ""; }, + EventSeries::Backward, + e)) { + return e.getLabel(); } + return ""; } @@ -221,7 +225,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - SparseOneDimensionalModel::PointList points = getLocalPoints(v, x); + EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { @@ -231,19 +235,19 @@ } } - sv_frame_t useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->getFrame(); RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); QString text; - if (points.begin()->label == "") { + if (points.begin()->getLabel() == "") { text = QString(tr("Time:\t%1\nNo label")) .arg(rt.toText(true).c_str()); } else { text = QString(tr("Time:\t%1\nLabel:\t%2")) .arg(rt.toText(true).c_str()) - .arg(points.begin()->label); + .arg(points.begin()->getLabel()); } pos = QPoint(v->getXForFrame(useFrame), pos.y()); @@ -259,67 +263,33 @@ return Layer::snapToFeatureFrame(v, frame, resolution, snap); } + // SnapLeft / SnapRight: return frame of nearest feature in that + // direction no matter how far away + // + // SnapNeighbouring: return frame of feature that would be used in + // an editing operation, i.e. closest feature in either direction + // but only if it is "close enough" + resolution = m_model->getResolution(); - SparseOneDimensionalModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); + EventVector points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; - } - - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; - - for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - - if (snap == SnapRight) { - - if (i->frame >= frame) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } - - } else { // nearest - - SparseOneDimensionalModel::PointList::const_iterator j = i; - ++j; - - if (j == points.end()) { - - snapped = i->frame; - found = true; - break; - - } else if (j->frame >= frame) { - - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } } - frame = snapped; - return found; + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event) { return true; }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e)) { + frame = e.getFrame(); + return true; + } + + return false; } void @@ -334,12 +304,11 @@ sv_frame_t frame0 = v->getFrameForX(x0); sv_frame_t frame1 = v->getFrameForX(x1); - SparseOneDimensionalModel::PointList points(m_model->getPoints - (frame0, frame1)); + EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0)); bool odd = false; if (m_plotStyle == PlotSegmentation && !points.empty()) { - int index = m_model->getIndexOf(*points.begin()); + int index = m_model->getRowForFrame(points.begin()->getFrame()); odd = ((index % 2) == 1); } @@ -372,31 +341,32 @@ sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { - SparseOneDimensionalModel::PointList localPoints = - getLocalPoints(v, localPos.x()); - if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; + EventVector localPoints = getLocalPoints(v, localPos.x()); + if (!localPoints.empty()) { + illuminateFrame = localPoints.begin()->getFrame(); + } } int prevX = -1; int textY = v->getTextLabelHeight(this, paint); - for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - const SparseOneDimensionalModel::Point &p(*i); - SparseOneDimensionalModel::PointList::const_iterator j = i; + Event p(*i); + EventVector::const_iterator j = i; ++j; - int x = v->getXForFrame(p.frame); + int x = v->getXForFrame(p.getFrame()); if (x == prevX && m_plotStyle == PlotInstants && - p.frame != illuminateFrame) continue; + p.getFrame() != illuminateFrame) continue; - int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x; + int iw = v->getXForFrame(p.getFrame() + m_model->getResolution()) - x; if (iw < 2) { if (iw < 1) { iw = 2; if (j != points.end()) { - int nx = v->getXForFrame(j->frame); + int nx = v->getXForFrame(j->getFrame()); if (nx < x + 3) iw = 1; } } else { @@ -404,7 +374,7 @@ } } - if (p.frame == illuminateFrame) { + if (p.getFrame() == illuminateFrame) { paint.setPen(getForegroundQColor(v->getView())); } else { paint.setPen(brushColour); @@ -424,15 +394,15 @@ int nx; if (j != points.end()) { - const SparseOneDimensionalModel::Point &q(*j); - nx = v->getXForFrame(q.frame); + Event q(*j); + nx = v->getXForFrame(q.getFrame()); } else { nx = v->getXForFrame(m_model->getEndFrame()); } if (nx >= x) { - if (illuminateFrame != p.frame && + if (illuminateFrame != p.getFrame() && (nx < x + 5 || x >= v->getPaintWidth() - 1)) { paint.setPen(Qt::NoPen); } @@ -445,22 +415,22 @@ paint.setPen(getBaseQColor()); - if (p.label != "") { + if (p.getLabel() != "") { // only draw if there's enough room from here to the next point - int lw = paint.fontMetrics().width(p.label); + int lw = paint.fontMetrics().width(p.getLabel()); bool good = true; if (j != points.end()) { - int nx = v->getXForFrame(j->frame); + int nx = v->getXForFrame(j->getFrame()); if (nx >= x && nx - x - iw - 3 <= lw) good = false; } if (good) { PaintAssistant::drawVisibleText(v, paint, x + iw + 2, textY, - p.label, + p.getLabel(), PaintAssistant::OutlinedText); } } @@ -482,12 +452,11 @@ if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point")); + m_editingPoint = Event(frame, tr("New Point")); if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, - tr("Draw Point")); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point")); + m_editingCommand->add(m_editingPoint); m_editing = true; } @@ -504,9 +473,9 @@ sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint.withFrame(frame); + m_editingCommand->add(m_editingPoint); } void @@ -517,7 +486,7 @@ #endif if (!m_model || !m_editing) return; QString newName = tr("Add Point at %1 s") - .arg(RealTime::frame2RealTime(m_editingPoint.frame, + .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(), m_model->getSampleRate()) .toText(false).c_str()); m_editingCommand->setName(newName); @@ -531,7 +500,7 @@ { if (!m_model) return; - SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; m_editingPoint = *points.begin(); @@ -556,15 +525,12 @@ m_editing = false; - SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; - if (points.begin()->frame != m_editingPoint.frame) return; + if (points.begin()->getFrame() != m_editingPoint.getFrame()) return; - m_editingCommand = new SparseOneDimensionalModel::EditCommand - (m_model, tr("Erase Point")); - - m_editingCommand->deletePoint(m_editingPoint); - + m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; @@ -579,7 +545,7 @@ if (!m_model) return; - SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; m_editingPoint = *points.begin(); @@ -606,13 +572,12 @@ frame = frame / m_model->getResolution() * m_model->getResolution(); if (!m_editingCommand) { - m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint.withFrame(frame); + m_editingCommand->add(m_editingPoint); } void @@ -624,7 +589,7 @@ if (!m_model || !m_editing) return; if (m_editingCommand) { QString newName = tr("Move Point to %1 s") - .arg(RealTime::frame2RealTime(m_editingPoint.frame, + .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(), m_model->getSampleRate()) .toText(false).c_str()); m_editingCommand->setName(newName); @@ -639,29 +604,29 @@ { if (!m_model) return false; - SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return false; - SparseOneDimensionalModel::Point point = *points.begin(); + Event point = *points.begin(); ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), ItemEditDialog::ShowTime | ItemEditDialog::ShowText); - dialog->setFrameTime(point.frame); - dialog->setText(point.label); + dialog->setFrameTime(point.getFrame()); + dialog->setText(point.getLabel()); if (dialog->exec() == QDialog::Accepted) { - SparseOneDimensionalModel::Point newPoint = point; - newPoint.frame = dialog->getFrameTime(); - newPoint.label = dialog->getText(); + Event newPoint = point + .withFrame(dialog->getFrameTime()) + .withLabel(dialog->getText()); - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point")); - command->deletePoint(point); - command->addPoint(newPoint); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Edit Point")); + command->remove(point); + command->add(newPoint); finish(command); } @@ -674,22 +639,17 @@ { if (!m_model) return; - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - SparseOneDimensionalModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + for (auto p: points) { + Event newPoint = p + .withFrame(p.getFrame() + newStartFrame - s.getStartFrame()); + command->remove(p); + command->add(newPoint); } finish(command); @@ -700,31 +660,24 @@ { if (!m_model) return; - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); - for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + for (auto p: points) { - if (s.contains(i->frame)) { + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; - - SparseOneDimensionalModel::Point newPoint(*i); - newPoint.frame = lrint(target); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -735,16 +688,14 @@ { if (!m_model) return; - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Delete Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selection")); - SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + for (auto p: points) { + command->remove(p); } finish(command); @@ -755,16 +706,11 @@ { if (!m_model) return; - SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->label); - point.setReferenceFrame(alignToReference(v, i->frame)); - to.addPoint(point); - } + for (auto p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -773,7 +719,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + EventVector points = from.getPoints(); bool realign = false; @@ -794,14 +740,12 @@ } } - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; - sv_frame_t frame = 0; if (!realign) { @@ -810,7 +754,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -824,14 +768,12 @@ else frame = 0; } - SparseOneDimensionalModel::Point newPoint(frame); - if (i->haveLabel()) { - newPoint.label = i->getLabel(); - } else if (i->haveValue()) { - newPoint.label = QString("%1").arg(i->getValue()); + Event newPoint = *i; + if (!i->hasLabel() && i->hasValue()) { + newPoint = newPoint.withLabel(QString("%1").arg(i->getValue())); } - command->addPoint(newPoint); + command->add(newPoint); } finish(command); diff -r 8d5bf4ab98ef -r 42c87368287c layer/TimeInstantLayer.h --- a/layer/TimeInstantLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TimeInstantLayer.h Fri May 17 10:02:52 2019 +0100 @@ -112,7 +112,7 @@ int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const override { return 0; } protected: - SparseOneDimensionalModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int) const; int getDefaultColourHint(bool dark, bool &impose) override; @@ -120,11 +120,11 @@ SparseOneDimensionalModel *m_model; bool m_editing; - SparseOneDimensionalModel::Point m_editingPoint; - SparseOneDimensionalModel::EditCommand *m_editingCommand; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; PlotStyle m_plotStyle; - void finish(SparseOneDimensionalModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c layer/TimeRulerLayer.cpp --- a/layer/TimeRulerLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TimeRulerLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -87,16 +87,6 @@ case SnapRight: frame = right; break; - - case SnapNearest: - { - if (llabs(frame - left) > llabs(right - frame)) { - frame = right; - } else { - frame = left; - } - break; - } case SnapNeighbouring: { diff -r 8d5bf4ab98ef -r 42c87368287c layer/TimeValueLayer.cpp --- a/layer/TimeValueLayer.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TimeValueLayer.cpp Fri May 17 10:02:52 2019 +0100 @@ -531,60 +531,68 @@ return mapper; } -SparseTimeValueModel::PointList +EventVector TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { - if (!m_model) return SparseTimeValueModel::PointList(); + if (!m_model) return {}; + // Return all points at a frame f, where f is the closest frame to + // pixel coordinate x whose pixel coordinate is both within a + // small (but somewhat arbitrary) fuzz distance from x and within + // the current view. If there is no such frame, return an empty + // vector. + sv_frame_t frame = v->getFrameForX(x); + + EventVector exact = m_model->getEventsStartingAt(frame); + if (!exact.empty()) return exact; - SparseTimeValueModel::PointList onPoints = - m_model->getPoints(frame); + // overspill == 1, so one event either side of the given span + EventVector neighbouring = m_model->getEventsWithin + (frame, m_model->getResolution(), 1); - if (!onPoints.empty()) { - return onPoints; - } - - SparseTimeValueModel::PointList prevPoints = - m_model->getPreviousPoints(frame); - SparseTimeValueModel::PointList nextPoints = - m_model->getNextPoints(frame); - - SparseTimeValueModel::PointList usePoints = prevPoints; - - if (prevPoints.empty()) { - usePoints = nextPoints; - } else if (nextPoints.empty()) { - // stick with prevPoints - } else if (prevPoints.begin()->frame < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; - } else if (nextPoints.begin()->frame - frame < - frame - prevPoints.begin()->frame) { - usePoints = nextPoints; - } - - if (!usePoints.empty()) { - double fuzz = v->scaleSize(2); - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 3)) { - usePoints.clear(); + double fuzz = v->scaleSize(2); + sv_frame_t suitable = 0; + bool have = false; + + for (Event e: neighbouring) { + sv_frame_t f = e.getFrame(); + if (f < v->getStartFrame() || f > v->getEndFrame()) { + continue; + } + int px = v->getXForFrame(f); + if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) { + continue; + } + if (!have) { + suitable = f; + have = true; + } else if (llabs(frame - f) < llabs(suitable - f)) { + suitable = f; } } - return usePoints; + if (have) { + return m_model->getEventsStartingAt(suitable); + } else { + return {}; + } } QString TimeValueLayer::getLabelPreceding(sv_frame_t frame) const { - if (!m_model) return ""; - SparseTimeValueModel::PointList points = m_model->getPreviousPoints(frame); - for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - if (i->label != "") return i->label; + if (!m_model || !m_model->hasTextLabels()) return ""; + + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event e) { return e.hasLabel() && e.getLabel() != ""; }, + EventSeries::Backward, + e)) { + return e.getLabel(); } + return ""; } @@ -595,7 +603,7 @@ if (!m_model || !m_model->getSampleRate()) return ""; - SparseTimeValueModel::PointList points = getLocalPoints(v, x); + EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { @@ -605,12 +613,12 @@ } } - sv_frame_t useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->getFrame(); RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); QString valueText; - float value = points.begin()->value; + float value = points.begin()->getValue(); QString unit = getScaleUnits(); if (unit == "Hz") { @@ -626,7 +634,7 @@ QString text; - if (points.begin()->label == "") { + if (points.begin()->getLabel() == "") { text = QString(tr("Time:\t%1\nValue:\t%2\nNo label")) .arg(rt.toText(true).c_str()) .arg(valueText); @@ -634,16 +642,17 @@ text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4")) .arg(rt.toText(true).c_str()) .arg(valueText) - .arg(points.begin()->label); + .arg(points.begin()->getLabel()); } pos = QPoint(v->getXForFrame(useFrame), - getYForValue(v, points.begin()->value)); + getYForValue(v, points.begin()->getValue())); return text; } bool -TimeValueLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, +TimeValueLayer::snapToFeatureFrame(LayerGeometryProvider *v, + sv_frame_t &frame, int &resolution, SnapType snap) const { @@ -651,71 +660,38 @@ return Layer::snapToFeatureFrame(v, frame, resolution, snap); } + // SnapLeft / SnapRight: return frame of nearest feature in that + // direction no matter how far away + // + // SnapNeighbouring: return frame of feature that would be used in + // an editing operation, i.e. closest feature in either direction + // but only if it is "close enough" + resolution = m_model->getResolution(); - SparseTimeValueModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); + EventVector points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; - frame = points.begin()->frame; + frame = points.begin()->getFrame(); return true; - } - - points = m_model->getPoints(frame, frame); - sv_frame_t snapped = frame; - bool found = false; - - for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { - - if (snap == SnapRight) { - - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } - - } else { // nearest - - SparseTimeValueModel::PointList::const_iterator j = i; - ++j; - - if (j == points.end()) { - - snapped = i->frame; - found = true; - break; - - } else if (j->frame >= frame) { - - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } } - frame = snapped; - return found; + Event e; + if (m_model->getNearestEventMatching + (frame, + [](Event) { return true; }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e)) { + frame = e.getFrame(); + return true; + } + + return false; } bool -TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, +TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, + sv_frame_t &frame, int &resolution, SnapType snap) const { @@ -723,76 +699,39 @@ return Layer::snapToSimilarFeature(v, frame, resolution, snap); } + // snap is only permitted to be SnapLeft or SnapRight here. + resolution = m_model->getResolution(); - const SparseTimeValueModel::PointList &points = m_model->getPoints(); - SparseTimeValueModel::PointList close = m_model->getPoints(frame, frame); + Event ref; + Event e; + float matchvalue; + bool found; - SparseTimeValueModel::PointList::const_iterator i; + found = m_model->getNearestEventMatching + (frame, [](Event) { return true; }, EventSeries::Backward, ref); - sv_frame_t matchframe = frame; - double matchvalue = 0.0; - - for (i = close.begin(); i != close.end(); ++i) { - if (i->frame > frame) break; - matchvalue = i->value; - matchframe = i->frame; + if (!found) { + return false; } - sv_frame_t snapped = frame; - bool found = false; - bool distant = false; - double epsilon = 0.0001; + matchvalue = ref.getValue(); + + found = m_model->getNearestEventMatching + (frame, + [matchvalue](Event e) { + double epsilon = 0.0001; + return fabs(e.getValue() - matchvalue) < epsilon; + }, + snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, + e); - i = close.begin(); - - // Scan through the close points first, then the more distant ones - // if no suitable close one is found. So the while-termination - // condition here can only happen once i has passed through the - // whole of the close container and then the whole of the separate - // points container. The two iterators are totally distinct, but - // have the same type so we cheekily use the same variable and a - // single loop for both. - - while (i != points.end()) { - - if (!distant) { - if (i == close.end()) { - // switch from the close container to the points container - i = points.begin(); - distant = true; - } - } - - if (snap == SnapRight) { - - if (i->frame > matchframe && - fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; - break; - } - - } else if (snap == SnapLeft) { - - if (i->frame < matchframe) { - if (fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } - } else if (found || distant) { - break; - } - - } else { - // no other snap types supported - } - - ++i; + if (!found) { + return false; } - frame = snapped; - return found; + frame = e.getFrame(); + return true; } void @@ -926,8 +865,7 @@ sv_frame_t frame1 = v->getFrameForX(x1); if (m_derivative) --frame0; - SparseTimeValueModel::PointList points(m_model->getPoints - (frame0, frame1)); + EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 1)); if (points.empty()) return; paint.setPen(getBaseQColor()); @@ -938,7 +876,7 @@ #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::paint: resolution is " - << m_model->getResolution() << " frames" << endl; + << m_model->getResolution() << " frames" << endl; #endif double min = m_model->getValueMinimum(); @@ -952,12 +890,13 @@ sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { - SparseTimeValueModel::PointList localPoints = - getLocalPoints(v, localPos.x()); + EventVector localPoints = getLocalPoints(v, localPos.x()); #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl; #endif - if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; + if (!localPoints.empty()) { + illuminateFrame = localPoints.begin()->getFrame(); + } } int w = @@ -990,21 +929,21 @@ sv_frame_t prevFrame = 0; - for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { if (m_derivative && i == points.begin()) continue; - const SparseTimeValueModel::Point &p(*i); + Event p(*i); - double value = p.value; + double value = p.getValue(); if (m_derivative) { - SparseTimeValueModel::PointList::const_iterator j = i; + EventVector::const_iterator j = i; --j; - value -= j->value; + value -= j->getValue(); } - int x = v->getXForFrame(p.frame); + int x = v->getXForFrame(p.getFrame()); int y = getYForValue(v, value); bool gap = false; @@ -1013,8 +952,8 @@ // Treat zeros as gaps continue; } - gap = (p.frame > prevFrame && - (p.frame - prevFrame >= m_model->getResolution() * 2)); + gap = (p.getFrame() > prevFrame && + (p.getFrame() - prevFrame >= m_model->getResolution() * 2)); } if (m_plotStyle != PlotSegmentation) { @@ -1031,20 +970,20 @@ int nx = v->getXForFrame(nf); int ny = y; - SparseTimeValueModel::PointList::const_iterator j = i; + EventVector::const_iterator j = i; ++j; if (j != points.end()) { - const SparseTimeValueModel::Point &q(*j); - nvalue = q.value; - if (m_derivative) nvalue -= p.value; - nf = q.frame; + Event q(*j); + nvalue = q.getValue(); + if (m_derivative) nvalue -= p.getValue(); + nf = q.getFrame(); nx = v->getXForFrame(nf); ny = getYForValue(v, nvalue); haveNext = true; } -// cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext +// cout << "frame = " << p.getFrame() << ", x = " << x << ", haveNext = " << haveNext // << ", nx = " << nx << endl; QPen pen(getBaseQColor()); @@ -1074,7 +1013,7 @@ bool illuminate = false; - if (illuminateFrame == p.frame) { + if (illuminateFrame == p.getFrame()) { // not equipped to illuminate the right section in line // or curve mode @@ -1138,7 +1077,7 @@ if (m_plotStyle == PlotDiscreteCurves) { bool nextGap = (nvalue == 0.0) || - (nf - p.frame >= m_model->getResolution() * 2); + (nf - p.getFrame() >= m_model->getResolution() * 2); if (nextGap) { x1 = x0; y1 = y0; @@ -1188,7 +1127,7 @@ if (v->shouldShowFeatureLabels()) { - QString label = p.label; + QString label = p.getLabel(); bool italic = false; if (label == "" && @@ -1196,7 +1135,7 @@ m_plotStyle == PlotSegmentation || m_plotStyle == PlotConnectedPoints)) { char lc[20]; - snprintf(lc, 20, "%.3g", p.value); + snprintf(lc, 20, "%.3g", p.getValue()); label = lc; italic = true; } @@ -1209,15 +1148,16 @@ if (haveRoom || (!haveNext && (pointCount == 0 || !italic))) { - PaintAssistant::drawVisibleText(v, paint, x + 5, textY, label, - italic ? - PaintAssistant::OutlinedItalicText : - PaintAssistant::OutlinedText); + PaintAssistant::drawVisibleText + (v, paint, x + 5, textY, label, + italic ? + PaintAssistant::OutlinedItalicText : + PaintAssistant::OutlinedText); } } } - prevFrame = p.frame; + prevFrame = p.getFrame(); ++pointCount; } @@ -1261,7 +1201,7 @@ void TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model || m_model->getPoints().empty()) return; + if (!m_model || m_model->isEmpty()) return; QString unit; double min, max; @@ -1328,13 +1268,13 @@ bool havePoint = false; - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (!points.empty()) { - for (SparseTimeValueModel::PointList::iterator i = points.begin(); + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - if (((i->frame / resolution) * resolution) != frame) { + if (((i->getFrame() / resolution) * resolution) != frame) { #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "ignoring out-of-range frame at " << i->frame << endl; + cerr << "ignoring out-of-range frame at " << i->getFrame() << endl; #endif continue; } @@ -1344,17 +1284,15 @@ } if (!havePoint) { - m_editingPoint = SparseTimeValueModel::Point - (frame, float(value), tr("New Point")); + m_editingPoint = Event(frame, float(value), tr("New Point")); } m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); - m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, - tr("Draw Point")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point")); if (!havePoint) { - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->add(m_editingPoint); } m_editing = true; @@ -1376,7 +1314,7 @@ double value = getValueForY(v, e->y()); - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); #ifdef DEBUG_TIME_VALUE_LAYER cerr << points.size() << " points" << endl; @@ -1385,41 +1323,41 @@ bool havePoint = false; if (!points.empty()) { - for (SparseTimeValueModel::PointList::iterator i = points.begin(); + for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { - if (i->frame == m_editingPoint.frame && - i->value == m_editingPoint.value) { + if (i->getFrame() == m_editingPoint.getFrame() && + i->getValue() == m_editingPoint.getValue()) { #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl; + cerr << "ignoring current editing point at " << i->getFrame() << ", " << i->getValue() << endl; #endif continue; } - if (((i->frame / resolution) * resolution) != frame) { + if (((i->getFrame() / resolution) * resolution) != frame) { #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "ignoring out-of-range frame at " << i->frame << endl; + cerr << "ignoring out-of-range frame at " << i->getFrame() << endl; #endif continue; } #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl; + cerr << "adjusting to new point at " << i->getFrame() << ", " << i->getValue() << endl; #endif m_editingPoint = *i; m_originalPoint = m_editingPoint; - m_editingCommand->deletePoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); havePoint = true; } } if (!havePoint) { - if (frame == m_editingPoint.frame) { - m_editingCommand->deletePoint(m_editingPoint); + if (frame == m_editingPoint.getFrame()) { + m_editingCommand->remove(m_editingPoint); } } -// m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.value = float(value); - m_editingCommand->addPoint(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(value)); + m_editingCommand->add(m_editingPoint); } void @@ -1439,7 +1377,7 @@ { if (!m_model) return; - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; m_editingPoint = *points.begin(); @@ -1464,16 +1402,13 @@ m_editing = false; - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; - if (points.begin()->frame != m_editingPoint.frame || - points.begin()->value != m_editingPoint.value) return; + if (points.begin()->getFrame() != m_editingPoint.getFrame() || + points.begin()->getValue() != m_editingPoint.getValue()) return; - m_editingCommand = new SparseTimeValueModel::EditCommand - (m_model, tr("Erase Point")); - - m_editingCommand->deletePoint(m_editingPoint); - + m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); + m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; @@ -1488,7 +1423,7 @@ if (!m_model) return; - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return; m_editingPoint = *points.begin(); @@ -1518,14 +1453,14 @@ double value = getValueForY(v, e->y()); if (!m_editingCommand) { - m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point")); } - m_editingCommand->deletePoint(m_editingPoint); - m_editingPoint.frame = frame; - m_editingPoint.value = float(value); - m_editingCommand->addPoint(m_editingPoint); + m_editingCommand->remove(m_editingPoint); + m_editingPoint = m_editingPoint + .withFrame(frame) + .withValue(float(value)); + m_editingCommand->add(m_editingPoint); } void @@ -1540,8 +1475,8 @@ QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { + if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { + if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Edit Point"); } else { newName = tr("Relocate Point"); @@ -1563,10 +1498,10 @@ { if (!m_model) return false; - SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + EventVector points = getLocalPoints(v, e->x()); if (points.empty()) return false; - SparseTimeValueModel::Point point = *points.begin(); + Event point = *points.begin(); ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), @@ -1575,21 +1510,21 @@ ItemEditDialog::ShowText, getScaleUnits()); - dialog->setFrameTime(point.frame); - dialog->setValue(point.value); - dialog->setText(point.label); + dialog->setFrameTime(point.getFrame()); + dialog->setValue(point.getValue()); + dialog->setText(point.getLabel()); if (dialog->exec() == QDialog::Accepted) { - SparseTimeValueModel::Point newPoint = point; - newPoint.frame = dialog->getFrameTime(); - newPoint.value = dialog->getValue(); - newPoint.label = dialog->getText(); + Event newPoint = point + .withFrame(dialog->getFrameTime()) + .withValue(dialog->getValue()) + .withLabel(dialog->getText()); - SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point")); - command->deletePoint(point); - command->addPoint(newPoint); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Edit Point")); + command->remove(point); + command->add(newPoint); finish(command); } @@ -1602,22 +1537,18 @@ { if (!m_model) return; - SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Drag Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Drag Selection")); - SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + for (Event p: points) { - if (s.contains(i->frame)) { - SparseTimeValueModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p.withFrame + (p.getFrame() + newStartFrame - s.getStartFrame()); + command->remove(p); + command->add(newPoint); } finish(command); @@ -1626,33 +1557,26 @@ void TimeValueLayer::resizeSelection(Selection s, Selection newSize) { - if (!m_model) return; + if (!m_model || !s.getDuration()) return; - SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Resize Selection")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Resize Selection")); - SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double ratio = double(newSize.getDuration()) / double(s.getDuration()); + double oldStart = double(s.getStartFrame()); + double newStart = double(newSize.getStartFrame()); - for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + for (Event p: points) { + + double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; - if (s.contains(i->frame)) { - - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; - - SparseTimeValueModel::Point newPoint(*i); - newPoint.frame = lrint(target); - command->deletePoint(*i); - command->addPoint(newPoint); - } + Event newPoint = p + .withFrame(lrint(newFrame)); + command->remove(p); + command->add(newPoint); } finish(command); @@ -1663,19 +1587,14 @@ { if (!m_model) return; - SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Delete Selected Points")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Delete Selected Points")); - SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - - if (s.contains(i->frame)) { - command->deletePoint(*i); - } + for (Event p: points) { + command->remove(p); } finish(command); @@ -1686,16 +1605,11 @@ { if (!m_model) return; - SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + EventVector points = + m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); - for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->value, i->label); - point.setReferenceFrame(alignToReference(v, i->frame)); - to.addPoint(point); - } + for (Event p: points) { + to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } @@ -1705,7 +1619,7 @@ { if (!m_model) return false; - const Clipboard::PointList &points = from.getPoints(); + EventVector points = from.getPoints(); bool realign = false; @@ -1726,8 +1640,8 @@ } } - SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); + ChangeEventsCommand *command = + new ChangeEventsCommand(m_model, tr("Paste")); enum ValueAvailability { UnknownAvailability, @@ -1746,18 +1660,16 @@ ValueAvailability availability = UnknownAvailability; - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; - if (availability == UnknownAvailability) { - if (i->haveValue()) availability = AllValues; + if (i->hasValue()) availability = AllValues; else availability = NoValues; continue; } - if (i->haveValue()) { + if (i->hasValue()) { if (availability == NoValues) { availability = SomeValues; } @@ -1768,7 +1680,7 @@ } if (!haveUsableLabels) { - if (i->haveLabel()) { + if (i->hasLabel()) { if (i->getLabel().contains(QRegExp("[0-9]"))) { haveUsableLabels = true; } @@ -1836,13 +1748,11 @@ } } - SparseTimeValueModel::Point prevPoint(0); + Event prevPoint; - for (Clipboard::PointList::const_iterator i = points.begin(); + for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { - if (!i->haveFrame()) continue; - sv_frame_t frame = 0; if (!realign) { @@ -1851,7 +1761,7 @@ } else { - if (i->haveReferenceFrame()) { + if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { @@ -1859,45 +1769,46 @@ } } - SparseTimeValueModel::Point newPoint(frame); - - if (i->haveLabel()) { - newPoint.label = i->getLabel(); - } else if (i->haveValue()) { - newPoint.label = QString("%1").arg(i->getValue()); + Event newPoint = *i; + if (!i->hasLabel() && i->hasValue()) { + newPoint = newPoint.withLabel(QString("%1").arg(i->getValue())); } bool usePrev = false; - SparseTimeValueModel::Point formerPrevPoint = prevPoint; + Event formerPrevPoint = prevPoint; - if (i->haveValue()) { - newPoint.value = i->getValue(); - } else { + if (!i->hasValue()) { #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "Setting value on point at " << newPoint.frame << " from labeller"; + cerr << "Setting value on point at " << newPoint.getFrame() << " from labeller"; if (i == points.begin()) { cerr << ", no prev point" << endl; } else { - cerr << ", prev point is at " << prevPoint.frame << endl; + cerr << ", prev point is at " << prevPoint.getFrame() << endl; } #endif - labeller.setValue + + Labeller::Revaluing valuing = + labeller.revalue (newPoint, (i == points.begin()) ? nullptr : &prevPoint); + #ifdef DEBUG_TIME_VALUE_LAYER - cerr << "New point value = " << newPoint.value << endl; + cerr << "New point value = " << newPoint.getValue() << endl; #endif - if (labeller.actingOnPrevPoint() && i != points.begin()) { + if (valuing.first == Labeller::AppliesToPreviousEvent) { usePrev = true; + prevPoint = valuing.second; + } else { + newPoint = valuing.second; } } if (usePrev) { - command->deletePoint(formerPrevPoint); - command->addPoint(prevPoint); + command->remove(formerPrevPoint); + command->add(prevPoint); } prevPoint = newPoint; - command->addPoint(newPoint); + command->add(newPoint); } finish(command); diff -r 8d5bf4ab98ef -r 42c87368287c layer/TimeValueLayer.h --- a/layer/TimeValueLayer.h Wed Apr 24 11:29:53 2019 +0100 +++ b/layer/TimeValueLayer.h Fri May 17 10:02:52 2019 +0100 @@ -174,15 +174,15 @@ void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const; bool shouldAutoAlign() const; - SparseTimeValueModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; + EventVector getLocalPoints(LayerGeometryProvider *v, int) const; int getDefaultColourHint(bool dark, bool &impose) override; SparseTimeValueModel *m_model; bool m_editing; - SparseTimeValueModel::Point m_originalPoint; - SparseTimeValueModel::Point m_editingPoint; - SparseTimeValueModel::EditCommand *m_editingCommand; + Event m_originalPoint; + Event m_editingPoint; + ChangeEventsCommand *m_editingCommand; int m_colourMap; bool m_colourInverted; PlotStyle m_plotStyle; @@ -193,7 +193,7 @@ mutable double m_scaleMinimum; mutable double m_scaleMaximum; - void finish(SparseTimeValueModel::EditCommand *command) { + void finish(ChangeEventsCommand *command) { Command *c = command->finish(); if (c) CommandHistory::getInstance()->addCommand(c, false); } diff -r 8d5bf4ab98ef -r 42c87368287c view/AlignmentView.cpp --- a/view/AlignmentView.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/view/AlignmentView.cpp Fri May 17 10:02:52 2019 +0100 @@ -165,10 +165,9 @@ vector keyFrames; - const SparseOneDimensionalModel::PointList pp = m->getPoints(); - for (SparseOneDimensionalModel::PointList::const_iterator pi = pp.begin(); - pi != pp.end(); ++pi) { - keyFrames.push_back(pi->frame); + EventVector pp = m->getAllEvents(); + for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) { + keyFrames.push_back(pi->getFrame()); } return keyFrames; @@ -193,8 +192,3 @@ return keyFrames; } - - - - - diff -r 8d5bf4ab98ef -r 42c87368287c view/Pane.cpp --- a/view/Pane.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/view/Pane.cpp Fri May 17 10:02:52 2019 +0100 @@ -2298,11 +2298,19 @@ // Coarse wheel information (or vertical zoom, which is // necessarily coarse itself) - // Sometimes on Linux we're seeing absurdly extreme angles on - // the first wheel event -- discard those entirely - if (abs(m_pendingWheelAngle) >= 600) { - m_pendingWheelAngle = 0; - return; + // Sometimes on Linux we're seeing very extreme angles on the + // first wheel event. They could be spurious, or they could be + // a result of the user frantically wheeling away while the + // pane was unresponsive for some reason. We don't want to + // discard them, as that makes the application feel even less + // responsive, but if we take them literally we risk changing + // the view so radically that the user won't recognise what + // has happened. Clamp them instead. + if (m_pendingWheelAngle > 600) { + m_pendingWheelAngle = 600; + } + if (m_pendingWheelAngle < -600) { + m_pendingWheelAngle = -600; } while (abs(m_pendingWheelAngle) >= 120) { diff -r 8d5bf4ab98ef -r 42c87368287c view/PaneStack.cpp --- a/view/PaneStack.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/view/PaneStack.cpp Fri May 17 10:02:52 2019 +0100 @@ -43,22 +43,33 @@ m_showAccessories(true), m_showAlignmentViews(false), m_splitter(new QSplitter), + m_autoResizeStack(new QWidget), m_propertyStackStack(new QStackedWidget), m_viewManager(viewManager), m_propertyStackMinWidth(100), - m_layoutStyle(PropertyStackPerPaneLayout) + m_layoutStyle(PropertyStackPerPaneLayout), + m_resizeMode(UserResizeable) { QHBoxLayout *layout = new QHBoxLayout; layout->setMargin(0); layout->setSpacing(0); + m_autoResizeLayout = new QVBoxLayout; + m_autoResizeLayout->setMargin(0); + m_autoResizeLayout->setSpacing(0); + m_autoResizeStack->setLayout(m_autoResizeLayout); + m_autoResizeStack->hide(); + layout->addWidget(m_autoResizeStack); + layout->setStretchFactor(m_autoResizeStack, 1); + m_splitter->setOrientation(Qt::Vertical); m_splitter->setOpaqueResize(false); - + m_splitter->show(); layout->addWidget(m_splitter); layout->setStretchFactor(m_splitter, 1); + + m_propertyStackStack->hide(); layout->addWidget(m_propertyStackStack); - m_propertyStackStack->hide(); setLayout(layout); } @@ -73,37 +84,44 @@ PaneStack::setShowAlignmentViews(bool show) { m_showAlignmentViews = show; - foreach (const PaneRec &r, m_panes) { - r.alignmentView->setVisible(m_showAlignmentViews); + // each alignment view shows alignment between the pane above and + // the pane it is attached to: so pane 0 doesn't have a visible one + for (int i = 1; in_range_for(m_panes, i); ++i) { + m_panes[i].alignmentView->setVisible(m_showAlignmentViews); } } Pane * PaneStack::addPane(bool suppressPropertyBox) { - return insertPane(getPaneCount(), suppressPropertyBox); -} - -Pane * -PaneStack::insertPane(int index, bool suppressPropertyBox) -{ QFrame *frame = new QFrame; QGridLayout *layout = new QGridLayout; layout->setMargin(0); - layout->setSpacing(2); + layout->setHorizontalSpacing(m_viewManager->scalePixelSize(2)); + if (m_showAlignmentViews) { + layout->setVerticalSpacing(0); + } else { + layout->setVerticalSpacing(m_viewManager->scalePixelSize(2)); + } + + AlignmentView *av = new AlignmentView(frame); + av->setFixedHeight(ViewManager::scalePixelSize(20)); + av->setViewManager(m_viewManager); + av->setVisible(false); // for now + layout->addWidget(av, 0, 1); QPushButton *xButton = new QPushButton(frame); xButton->setIcon(IconLoader().load("cross")); xButton->setFixedSize(QSize(16, 16)); xButton->setFlat(true); xButton->setVisible(m_showAccessories); - layout->addWidget(xButton, 0, 0); + layout->addWidget(xButton, 1, 0); connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked())); ClickableLabel *currentIndicator = new ClickableLabel(frame); connect(currentIndicator, SIGNAL(clicked()), this, SLOT(indicatorClicked())); - layout->addWidget(currentIndicator, 1, 0); + layout->addWidget(currentIndicator, 2, 0); layout->setRowStretch(1, 20); currentIndicator->setMinimumWidth(8); currentIndicator->setScaledContents(true); @@ -120,15 +138,9 @@ } else { pane->setViewManager(m_viewManager); } - layout->addWidget(pane, 0, 1, 2, 1); + layout->addWidget(pane, 1, 1, 2, 1); layout->setColumnStretch(1, 20); - AlignmentView *av = new AlignmentView(frame); - av->setFixedHeight(40);//!!! - av->setVisible(m_showAlignmentViews); - av->setViewManager(m_viewManager); - layout->addWidget(av, 2, 1); - QWidget *properties = nullptr; if (suppressPropertyBox) { properties = new QFrame(); @@ -142,7 +154,7 @@ this, SIGNAL(contextHelpChanged(const QString &))); } if (m_layoutStyle == PropertyStackPerPaneLayout) { - layout->addWidget(properties, 0, 2, 2, 1); + layout->addWidget(properties, 1, 2, 2, 1); } else { properties->setParent(m_propertyStackStack); m_propertyStackStack->addWidget(properties); @@ -160,7 +172,13 @@ m_panes.push_back(rec); frame->setLayout(layout); - m_splitter->insertWidget(index, frame); + + if (m_resizeMode == UserResizeable) { + m_splitter->addWidget(frame); + } else { + m_autoResizeLayout->addWidget(frame); + frame->adjustSize(); + } connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)), this, SLOT(propertyContainerAdded(PropertyContainer *))); @@ -193,20 +211,19 @@ void PaneStack::relinkAlignmentViews() { - for (int i = 0; i < (int)m_panes.size(); ++i) { - m_panes[i].alignmentView->setViewAbove(m_panes[i].pane); - if (i + 1 < (int)m_panes.size()) { - m_panes[i].alignmentView->setViewBelow(m_panes[i+1].pane); - } else { - m_panes[i].alignmentView->setViewBelow(nullptr); - } + if (m_panes.empty()) return; + m_panes[0].alignmentView->hide(); + for (int i = 1; in_range_for(m_panes, i); ++i) { + m_panes[i].alignmentView->setViewAbove(m_panes[i-1].pane); + m_panes[i].alignmentView->setViewBelow(m_panes[i].pane); + m_panes[i].alignmentView->setVisible(true); } } void PaneStack::unlinkAlignmentViews() { - for (int i = 0; i < (int)m_panes.size(); ++i) { + for (int i = 0; in_range_for(m_panes, i); ++i) { m_panes[i].alignmentView->setViewAbove(nullptr); m_panes[i].alignmentView->setViewBelow(nullptr); } @@ -406,13 +423,12 @@ showOrHidePaneAccessories(); emit paneHidden(pane); emit paneHidden(); + relinkAlignmentViews(); return; } ++i; } - relinkAlignmentViews(); - SVCERR << "WARNING: PaneStack::hidePane(" << pane << "): Pane not found in visible panes" << endl; } @@ -431,14 +447,13 @@ //!!! update current pane showOrHidePaneAccessories(); + relinkAlignmentViews(); return; } ++i; } - relinkAlignmentViews(); - SVCERR << "WARNING: PaneStack::showPane(" << pane << "): Pane not found in hidden panes" << endl; } @@ -655,6 +670,10 @@ void PaneStack::sizePanesEqually() { + if (m_resizeMode == AutoResizeOnly) { + return; + } + QList sizes = m_splitter->sizes(); if (sizes.empty()) return; @@ -712,3 +731,19 @@ m_splitter->setSizes(sizes); } +void +PaneStack::setResizeMode(ResizeMode mode) +{ + if (mode == UserResizeable) { + m_autoResizeStack->hide(); + m_splitter->show(); + } else { + m_autoResizeStack->show(); + m_splitter->hide(); + } + m_resizeMode = mode; + + // we don't actually move any existing panes yet! let's do that + // only if we turn out to need it, shall we? +} + diff -r 8d5bf4ab98ef -r 42c87368287c view/PaneStack.h --- a/view/PaneStack.h Wed Apr 24 11:29:53 2019 +0100 +++ b/view/PaneStack.h Fri May 17 10:02:52 2019 +0100 @@ -25,6 +25,7 @@ class QWidget; class QLabel; class QStackedWidget; +class QVBoxLayout; class QSplitter; class QGridLayout; class QPushButton; @@ -41,10 +42,10 @@ Q_OBJECT public: - PaneStack(QWidget *parent, ViewManager *viewManager); + PaneStack(QWidget *parent, + ViewManager *viewManager); Pane *addPane(bool suppressPropertyBox = false); // I own the returned value - Pane *insertPane(int index, bool suppressPropertyBox = false); // I own the returned value void deletePane(Pane *pane); // Deletes the pane, but _not_ its layers int getPaneCount() const; // Returns only count of visible panes @@ -70,6 +71,14 @@ LayoutStyle getLayoutStyle() const { return m_layoutStyle; } void setLayoutStyle(LayoutStyle style); + enum ResizeMode { + UserResizeable = 0, + AutoResizeOnly = 1 + }; + + ResizeMode getResizeMode() const { return m_resizeMode; } + void setResizeMode(ResizeMode); + void setPropertyStackMinWidth(int mw); void setShowPaneAccessories(bool show); // current indicator, close button @@ -132,7 +141,10 @@ bool m_showAccessories; bool m_showAlignmentViews; - QSplitter *m_splitter; + QSplitter *m_splitter; // constitutes the stack in UserResizeable mode + QWidget *m_autoResizeStack; // constitutes the stack in AutoResizeOnly mode + QVBoxLayout *m_autoResizeLayout; + QStackedWidget *m_propertyStackStack; ViewManager *m_viewManager; // I don't own this @@ -145,6 +157,7 @@ void relinkAlignmentViews(); LayoutStyle m_layoutStyle; + ResizeMode m_resizeMode; }; #endif diff -r 8d5bf4ab98ef -r 42c87368287c view/View.cpp --- a/view/View.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/view/View.cpp Fri May 17 10:02:52 2019 +0100 @@ -50,6 +50,7 @@ //#define DEBUG_VIEW 1 //#define DEBUG_VIEW_WIDGET_PAINT 1 +//#define DEBUG_PROGRESS_STUFF 1 View::View(QWidget *w, bool showProgress) : QFrame(w), @@ -234,7 +235,7 @@ for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->needsTextLabelHeight()) { - sortedLayers[getObjectExportId(*i)] = *i; + sortedLayers[(*i)->getExportId()] = *i; } } @@ -1736,9 +1737,21 @@ void View::checkProgress(void *object) { - if (!m_showProgress) return; - + if (!m_showProgress) { +#ifdef DEBUG_PROGRESS_STUFF + SVCERR << "View[" << this << "]::checkProgress(" << object << "): " + << "m_showProgress is off" << endl; +#endif + return; + } + + QSettings settings; + settings.beginGroup("View"); + bool showCancelButton = settings.value("showcancelbuttons", true).toBool(); + settings.endGroup(); + int ph = height(); + bool found = false; for (ProgressMap::iterator i = m_progressBars.begin(); i != m_progressBars.end(); ++i) { @@ -1748,6 +1761,18 @@ if (i->first == object) { + found = true; + + if (i->first->isLayerDormant(this)) { + // A dormant (invisible) layer can still be busy + // generating, but we don't usually want to indicate + // it because it probably means it's a duplicate of a + // visible layer + cancel->hide(); + pb->hide(); + continue; + } + // The timer is used to test for stalls. If the progress // bar does not get updated for some length of time, the // timer prompts it to go back into "indeterminate" mode @@ -1757,6 +1782,12 @@ QString text = i->first->getPropertyContainerName(); QString error = i->first->getError(this); +#ifdef DEBUG_PROGRESS_STUFF + SVCERR << "View[" << this << "]::checkProgress(" << object << "): " + << "found progress bar " << pb << " for layer at height " << ph + << ": completion = " << completion << endl; +#endif + if (error != "" && error != m_lastError) { QMessageBox::critical(this, tr("Layer rendering error"), error); m_lastError = error; @@ -1778,7 +1809,12 @@ (wfm = dynamic_cast (model->getSourceModel())))) { completion = wfm->getAlignmentCompletion(); -// SVDEBUG << "View::checkProgress: Alignment completion = " << completion << endl; + +#ifdef DEBUG_PROGRESS_STUFF + SVCERR << "View[" << this << "]::checkProgress(" << object << "): " + << "alignment completion = " << completion << endl; +#endif + if (completion < 100) { text = tr("Alignment"); } @@ -1796,21 +1832,29 @@ } else { -// cerr << "progress = " << completion << endl; - if (!pb->isVisible()) { i->second.lastCheck = 0; timer->setInterval(2000); timer->start(); } - int scaled20 = scalePixelSize(20); - - cancel->move(0, ph - pb->height()/2 - scaled20/2); - cancel->show(); - - pb->setValue(completion); - pb->move(scaled20, ph - pb->height()); + if (showCancelButton) { + + int scaled20 = scalePixelSize(20); + + cancel->move(0, ph - pb->height()/2 - scaled20/2); + cancel->show(); + + pb->setValue(completion); + pb->move(scaled20, ph - pb->height()); + + } else { + + cancel->hide(); + + pb->setValue(completion); + pb->move(0, ph - pb->height()); + } pb->show(); pb->update(); @@ -1823,6 +1867,14 @@ } } } + + if (!found) { +#ifdef DEBUG_PROGRESS_STUFF + SVCERR << "View[" << this << "]::checkProgress(" << object << "): " + << "failed to find layer " << object << " in progress map" + << endl; +#endif + } } void @@ -1831,9 +1883,16 @@ QObject *s = sender(); QTimer *t = qobject_cast(s); if (!t) return; + for (ProgressMap::iterator i = m_progressBars.begin(); i != m_progressBars.end(); ++i) { + if (i->second.checkTimer == t) { + +#ifdef DEBUG_PROGRESS_STUFF + SVCERR << "View[" << this << "]::progressCheckStalledTimerElapsed for layer " << i->first << endl; +#endif + int value = i->second.bar->value(); if (value > 0 && value == i->second.lastCheck) { i->second.bar->setMaximum(0); // indeterminate diff -r 8d5bf4ab98ef -r 42c87368287c widgets/AudioDial.cpp --- a/widgets/AudioDial.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/widgets/AudioDial.cpp Fri May 17 10:02:52 2019 +0100 @@ -74,8 +74,8 @@ // Constructor. AudioDial::AudioDial(QWidget *parent) : QDial(parent), - m_knobColor(Qt::black), - m_meterColor(Qt::white), + m_knobColor(Qt::black), // shorthand for "background colour" in paint() + m_meterColor(Qt::white), // shorthand for "foreground colour" in paint() m_defaultValue(0), m_defaultMappedValue(0), m_mappedValue(0), @@ -131,15 +131,28 @@ int numTicks = 1 + (maximum() + ns - minimum()) / ns; QColor knobColor(m_knobColor); - if (knobColor == Qt::black) - knobColor = palette().window().color(); + if (knobColor == Qt::black) { + knobColor = palette().window().color().light(150); + } + bool knobIsDark = + (knobColor.red() + knobColor.green() + knobColor.blue() <= 384); QColor meterColor(m_meterColor); - if (!isEnabled()) + if (!isEnabled()) { meterColor = palette().mid().color(); - else if (m_meterColor == Qt::white) - meterColor = palette().highlight().color(); + } else if (m_meterColor == Qt::white) { + if (knobIsDark) { + meterColor = palette().light().color(); + } else { + meterColor = palette().highlight().color(); + } + } + QColor notchColor(palette().dark().color()); + if (knobIsDark) { + notchColor = palette().light().color(); + } + int m_size = width() < height() ? width() : height(); int scale = 1; int width = m_size - 2*scale; @@ -169,7 +182,11 @@ int pos = indent-1 + (width-2*indent) / 20; int darkWidth = (width-2*indent) * 3 / 4; while (darkWidth) { - c = c.light(102); + if (knobIsDark) { + c = c.dark(102); + } else { + c = c.light(102); + } pen.setColor(c); paint.setPen(pen); paint.drawEllipse(pos, pos, darkWidth, darkWidth); @@ -184,7 +201,7 @@ if ( notchesVisible() ) { // cerr << "Notches visible" << endl; - pen.setColor(palette().dark().color()); + pen.setColor(notchColor); pen.setWidth(scale); paint.setPen(pen); for (int i = 0; i < numTicks; ++i) { @@ -220,7 +237,11 @@ // Knob shadow... int shadowAngle = -720; - c = knobColor.dark(); + if (knobIsDark) { + c = knobColor.light(); + } else { + c = knobColor.dark(); + } for (int arc = 120; arc < 2880; arc += 240) { pen.setColor(c); paint.setPen(pen); @@ -228,7 +249,11 @@ width-2*indent, width-2*indent, shadowAngle + arc, 240); paint.drawArc(indent, indent, width-2*indent, width-2*indent, shadowAngle - arc, 240); - c = c.light(110); + if (knobIsDark) { + c = c.dark(110); + } else { + c = c.light(110); + } } // Scale shadow, omitting the bottom part... @@ -255,7 +280,11 @@ // Scale ends... - pen.setColor(palette().shadow().color()); + if (knobIsDark) { + pen.setColor(palette().mid().color()); + } else { + pen.setColor(palette().shadow().color()); + } pen.setWidth(scale); paint.setPen(pen); for (int i = 0; i < numTicks; ++i) { @@ -278,8 +307,15 @@ double x = hyp - len * sin(angle); double y = hyp + len * cos(angle); - c = palette().dark().color(); - pen.setColor(isEnabled() ? c.dark(130) : c); + c = notchColor; + if (isEnabled()) { + if (knobIsDark) { + c = c.light(130); + } else { + c = c.dark(130); + } + } + pen.setColor(c); pen.setWidth(scale * 2); paint.setPen(pen); paint.drawLine(int(x0), int(y0), int(x), int(y)); diff -r 8d5bf4ab98ef -r 42c87368287c widgets/IconLoader.cpp --- a/widgets/IconLoader.cpp Wed Apr 24 11:29:53 2019 +0100 +++ b/widgets/IconLoader.cpp Fri May 17 10:02:52 2019 +0100 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,11 @@ bool IconLoader::shouldInvert() const { + QSettings settings; + settings.beginGroup("IconLoader"); + if (!settings.value("invert-icons-on-dark-background", true).toBool()) { + return false; + } QColor bg = QApplication::palette().window().color(); bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384); return darkBackground;