# HG changeset patch # User Chris Cannam # Date 1537188691 -3600 # Node ID 13d9b422f7fef21a013329d081123e7191401824 # Parent 57d192e2633160cdf035cf5f7bccd19404d56e41# Parent 8068a0bee55058a15603dc4b6272a3dcba2044d9 Merge from default branch diff -r 57d192e26331 -r 13d9b422f7fe files.pri --- a/files.pri Mon Dec 12 15:18:52 2016 +0000 +++ b/files.pri Mon Sep 17 13:51:31 2018 +0100 @@ -7,6 +7,8 @@ layer/ColourScale.h \ layer/ColourScaleLayer.h \ layer/FlexiNoteLayer.h \ + layer/HorizontalFrequencyScale.h \ + layer/HorizontalScaleProvider.h \ layer/ImageLayer.h \ layer/ImageRegionFinder.h \ layer/Layer.h \ @@ -44,8 +46,11 @@ widgets/ActivityLog.h \ widgets/AudioDial.h \ widgets/ClickableLabel.h \ + widgets/ColourComboBox.h \ + widgets/ColourMapComboBox.h \ widgets/ColourNameDialog.h \ widgets/CommandHistory.h \ + widgets/CSVAudioFormatDialog.h \ widgets/CSVFormatDialog.h \ widgets/Fader.h \ widgets/InteractiveFileFinder.h \ @@ -66,9 +71,12 @@ widgets/NotifyingComboBox.h \ widgets/NotifyingPushButton.h \ widgets/NotifyingTabBar.h \ + widgets/NotifyingToolButton.h \ widgets/Panner.h \ widgets/PluginParameterBox.h \ widgets/PluginParameterDialog.h \ + widgets/PluginPathConfigurator.h \ + widgets/PluginReviewDialog.h \ widgets/ProgressDialog.h \ widgets/PropertyBox.h \ widgets/PropertyStack.h \ @@ -80,6 +88,8 @@ widgets/TipDialog.h \ widgets/TransformFinder.h \ widgets/UnitConverter.h \ + widgets/WheelCounter.h \ + widgets/WidgetScale.h \ widgets/WindowShapePreview.h \ widgets/WindowTypeSelector.h @@ -90,6 +100,7 @@ layer/ColourMapper.cpp \ layer/ColourScale.cpp \ layer/FlexiNoteLayer.cpp \ + layer/HorizontalFrequencyScale.cpp \ layer/ImageLayer.cpp \ layer/ImageRegionFinder.cpp \ layer/Layer.cpp \ @@ -121,8 +132,11 @@ view/ViewManager.cpp \ widgets/ActivityLog.cpp \ widgets/AudioDial.cpp \ + widgets/ColourComboBox.cpp \ + widgets/ColourMapComboBox.cpp \ widgets/ColourNameDialog.cpp \ widgets/CommandHistory.cpp \ + widgets/CSVAudioFormatDialog.cpp \ widgets/CSVFormatDialog.cpp \ widgets/Fader.cpp \ widgets/InteractiveFileFinder.cpp \ @@ -143,9 +157,12 @@ widgets/NotifyingComboBox.cpp \ widgets/NotifyingPushButton.cpp \ widgets/NotifyingTabBar.cpp \ + widgets/NotifyingToolButton.cpp \ widgets/Panner.cpp \ widgets/PluginParameterBox.cpp \ widgets/PluginParameterDialog.cpp \ + widgets/PluginPathConfigurator.cpp \ + widgets/PluginReviewDialog.cpp \ widgets/ProgressDialog.cpp \ widgets/PropertyBox.cpp \ widgets/PropertyStack.cpp \ diff -r 57d192e26331 -r 13d9b422f7fe layer/Colour3DPlotLayer.cpp --- a/layer/Colour3DPlotLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Colour3DPlotLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -70,6 +70,7 @@ Colour3DPlotLayer::~Colour3DPlotLayer() { invalidateRenderers(); + if (m_peakCache) m_peakCache->aboutToDelete(); delete m_peakCache; } @@ -106,7 +107,7 @@ switch (value) { default: case 0: return { ColumnNormalization::None, false }; - case 1: return { ColumnNormalization::Max1, false }; + case 1: return { ColumnNormalization::Range01, false }; case 2: return { ColumnNormalization::None, true }; // visible area case 3: return { ColumnNormalization::Hybrid, false }; } @@ -118,10 +119,11 @@ if (visible) return 2; switch (norm) { case ColumnNormalization::None: return 0; - case ColumnNormalization::Max1: return 1; + case ColumnNormalization::Range01: return 1; case ColumnNormalization::Hybrid: return 3; case ColumnNormalization::Sum1: + case ColumnNormalization::Max1: default: return 0; } } @@ -135,6 +137,8 @@ void Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model) { + SVDEBUG << "Colour3DPlotLayer::setModel(" << model << ")" << endl; + if (m_model == model) return; const DenseThreeDimensionalModel *oldModel = m_model; m_model = model; @@ -144,7 +148,7 @@ connect(m_model, SIGNAL(modelChanged()), this, SLOT(modelChanged())); connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); + this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); m_peakResolution = 256; if (model->getResolution() > 512) { @@ -155,10 +159,12 @@ m_peakResolution = 128; } + if (m_peakCache) m_peakCache->aboutToDelete(); delete m_peakCache; m_peakCache = 0; invalidateRenderers(); + invalidateMagnitudes(); emit modelReplaced(); emit sliceableModelReplaced(oldModel, model); @@ -168,6 +174,7 @@ Colour3DPlotLayer::cacheInvalid() { invalidateRenderers(); + invalidateMagnitudes(); } void @@ -175,10 +182,12 @@ sv_frame_t /* endFrame */) { //!!! should do this only if the range is visible + if (m_peakCache) m_peakCache->aboutToDelete(); delete m_peakCache; m_peakCache = 0; invalidateRenderers(); + invalidateMagnitudes(); } void @@ -191,6 +200,15 @@ m_renderers.clear(); } +void +Colour3DPlotLayer::invalidateMagnitudes() +{ +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "Colour3DPlotLayer::invalidateMagnitudes called" << endl; +#endif + m_viewMags.clear(); +} + Dense3DModelPeakCache * Colour3DPlotLayer::getPeakCache() const { @@ -275,6 +293,7 @@ if (name == "Invert Vertical Scale") return ToggleProperty; if (name == "Opaque") return ToggleProperty; if (name == "Smooth") return ToggleProperty; + if (name == "Colour") return ColourMapProperty; return ValueProperty; } @@ -305,36 +324,36 @@ if (name == "Gain") { - *min = -50; - *max = 50; + *min = -50; + *max = 50; *deflt = int(lrint(log10(1.0) * 20.0)); - if (*deflt < *min) *deflt = *min; - if (*deflt > *max) *deflt = *max; + if (*deflt < *min) *deflt = *min; + if (*deflt > *max) *deflt = *max; - val = int(lrint(log10(m_gain) * 20.0)); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(log10(m_gain) * 20.0)); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Colour Scale") { // linear, log, +/-1, abs - *min = 0; - *max = 3; + *min = 0; + *max = 3; *deflt = 0; - val = convertFromColourScale(m_colourScale); + val = convertFromColourScale(m_colourScale); } else if (name == "Colour") { - *min = 0; - *max = ColourMapper::getColourMapCount() - 1; + *min = 0; + *max = ColourMapper::getColourMapCount() - 1; *deflt = 0; - val = m_colourMap; + val = m_colourMap; } else if (name == "Normalization") { - + *min = 0; *max = 3; *deflt = 0; @@ -342,29 +361,29 @@ val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea); } else if (name == "Invert Vertical Scale") { - + *deflt = 0; - val = (m_invertVertical ? 1 : 0); + val = (m_invertVertical ? 1 : 0); } else if (name == "Bin Scale") { - *min = 0; - *max = 1; + *min = 0; + *max = 1; *deflt = int(BinScale::Linear); - val = (int)m_binScale; + val = (int)m_binScale; } else if (name == "Opaque") { - + *deflt = 0; - val = (m_opaque ? 1 : 0); + val = (m_opaque ? 1 : 0); } else if (name == "Smooth") { - + *deflt = 0; - val = (m_smooth ? 1 : 0); + val = (m_smooth ? 1 : 0); } else { - val = Layer::getPropertyRangeAndValue(name, min, max, deflt); + val = Layer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -372,29 +391,36 @@ QString Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Colour") { return ColourMapper::getColourMapName(value); } if (name == "Colour Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Log"); - case 2: return tr("+/-1"); - case 3: return tr("Absolute"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + case 2: return tr("+/-1"); + case 3: return tr("Absolute"); + } } if (name == "Normalization") { - return ""; // icon only + switch(value) { + default: + case 0: return tr("None"); + case 1: return tr("Col"); + case 2: return tr("View"); + case 3: return tr("Hybrid"); + } +// return ""; // icon only } if (name == "Bin Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Log"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + } } return tr(""); } @@ -428,23 +454,23 @@ Colour3DPlotLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(float(pow(10, value/20.0))); + setGain(float(pow(10, value/20.0))); } else if (name == "Colour Scale") { setColourScale(convertToColourScale(value)); } else if (name == "Colour") { setColourMap(value); } else if (name == "Invert Vertical Scale") { - setInvertVertical(value ? true : false); + setInvertVertical(value ? true : false); } else if (name == "Opaque") { - setOpaque(value ? true : false); + setOpaque(value ? true : false); } else if (name == "Smooth") { - setSmooth(value ? true : false); + setSmooth(value ? true : false); } else if (name == "Bin Scale") { - switch (value) { - default: - case 0: setBinScale(BinScale::Linear); break; - case 1: setBinScale(BinScale::Log); break; - } + switch (value) { + default: + case 0: setBinScale(BinScale::Linear); break; + case 1: setBinScale(BinScale::Log); break; + } } else if (name == "Normalization") { auto n = convertToColumnNorm(value); setNormalization(n.first); @@ -455,9 +481,9 @@ void Colour3DPlotLayer::setColourScale(ColourScaleType scale) { + m_colourScaleSet = true; // even if setting to the same thing if (m_colourScale == scale) return; m_colourScale = scale; - m_colourScaleSet = true; invalidateRenderers(); emit layerParametersChanged(); } @@ -523,8 +549,9 @@ { if (m_normalizeVisibleArea == n) return; + invalidateRenderers(); + invalidateMagnitudes(); m_normalizeVisibleArea = n; - invalidateRenderers(); emit layerParametersChanged(); } @@ -597,7 +624,7 @@ Layer::setLayerDormant(v, true); cacheInvalid(); - + } else { Layer::setLayerDormant(v, false); @@ -607,17 +634,7 @@ bool Colour3DPlotLayer::isLayerScrollable(const LayerGeometryProvider * /* v */) const { - if (m_normalizeVisibleArea) { - return false; - } - //!!! ah hang on, if we're potentially rendering incrementally - //!!! they we can't be scrollable return false; -// if (getRenderer(v)->willRenderOpaque(v)) { -// return true; -// } -// QPoint discard; -// return !v->shouldIlluminateLocalFeatures(this, discard); } bool @@ -629,7 +646,7 @@ min = 0; max = double(m_model->getHeight()); - logarithmic = false; + logarithmic = (m_binScale == BinScale::Log); unit = ""; return true; @@ -753,11 +770,15 @@ getDisplayExtents(mn, mx); double h = v->getPaintHeight(); if (m_binScale == BinScale::Linear) { - bin = mn + ((h - y) * (mx - mn)) / h; + // Arrange that the first bin (mn) appears as the exact result + // for the first pixel (which is pixel h-1) and the first + // out-of-range bin (mx) would appear as the exact result for + // the first out-of-range pixel (which would be pixel -1) + bin = mn + ((h - y - 1) * (mx - mn)) / h; } else { double logmin = mn + 1, logmax = mx + 1; LogRange::mapRange(logmin, logmax); - bin = LogRange::unmap(logmin + ((h - y) * (logmax - logmin)) / h) - 1; + bin = LogRange::unmap(logmin + ((h - y - 1) * (logmax - logmin)) / h) - 1; } return bin; } @@ -814,12 +835,12 @@ else binName = QString("%1 [%2]").arg(binName).arg(sy + 1); QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4") - .arg(RealTime::frame2RealTime(f0, m_model->getSampleRate()) - .toText(true).c_str()) - .arg(RealTime::frame2RealTime(f1, m_model->getSampleRate()) - .toText(true).c_str()) - .arg(binName) - .arg(value); + .arg(RealTime::frame2RealTime(f0, m_model->getSampleRate()) + .toText(true).c_str()) + .arg(RealTime::frame2RealTime(f1, m_model->getSampleRate()) + .toText(true).c_str()) + .arg(binName) + .arg(value); return text; } @@ -842,13 +863,13 @@ bool another = false; for (int i = 0; i < m_model->getHeight(); ++i) { - if (m_model->getBinName(i).length() > sampleText.length()) { - sampleText = m_model->getBinName(i); + if (m_model->getBinName(i).length() > sampleText.length()) { + sampleText = m_model->getBinName(i); another = true; - } + } } if (another) { - tw = std::max(tw, paint.fontMetrics().width(sampleText)); + tw = std::max(tw, paint.fontMetrics().width(sampleText)); } return tw + 13 + getColourScaleWidth(paint); @@ -948,7 +969,7 @@ } } } - + py = y0; if (i < symax) { @@ -973,80 +994,52 @@ paint.restore(); } -DenseThreeDimensionalModel::Column -Colour3DPlotLayer::getColumn(int col) const -{ - Profiler profiler("Colour3DPlotLayer::getColumn"); - - DenseThreeDimensionalModel::Column values = m_model->getColumn(col); - values.resize(m_model->getHeight(), 0.f); - if (m_normalization != ColumnNormalization::Max1 && - m_normalization != ColumnNormalization::Hybrid) { - return values; - } - - double colMax = 0.f, colMin = 0.f; - double min = 0.f, max = 0.f; - - int nv = int(values.size()); - - min = m_model->getMinimumLevel(); - max = m_model->getMaximumLevel(); - - for (int y = 0; y < nv; ++y) { - if (y == 0 || values.at(y) > colMax) colMax = values.at(y); - if (y == 0 || values.at(y) < colMin) colMin = values.at(y); - } - if (colMin == colMax) colMax = colMin + 1; - - for (int y = 0; y < nv; ++y) { - - double value = values.at(y); - double norm = (value - colMin) / (colMax - colMin); - double newvalue = min + (max - min) * norm; - - if (value != newvalue) values[y] = float(newvalue); - } - - if (m_normalization == ColumnNormalization::Hybrid - && (colMax > 0.0)) { - double logmax = log10(colMax); - for (int y = 0; y < nv; ++y) { - values[y] = float(values[y] * logmax); - } - } - - return values; -} - Colour3DPlotRenderer * Colour3DPlotLayer::getRenderer(const LayerGeometryProvider *v) const { - if (m_renderers.find(v->getId()) == m_renderers.end()) { + int viewId = v->getId(); + + if (m_renderers.find(viewId) == m_renderers.end()) { Colour3DPlotRenderer::Sources sources; sources.verticalBinLayer = this; sources.fft = 0; sources.source = m_model; - sources.peakCache = getPeakCache(); + sources.peakCaches.push_back(getPeakCache()); ColourScale::Parameters cparams; cparams.colourMap = m_colourMap; cparams.scaleType = m_colourScale; cparams.gain = m_gain; - if (m_normalization == ColumnNormalization::None) { - cparams.minValue = m_model->getMinimumLevel(); - cparams.maxValue = m_model->getMaximumLevel(); + double minValue = 0.0; + double maxValue = 1.0; + + if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) { + minValue = m_viewMags[viewId].getMin(); + maxValue = m_viewMags[viewId].getMax(); } else if (m_normalization == ColumnNormalization::Hybrid) { - cparams.minValue = 0; - cparams.maxValue = log10(m_model->getMaximumLevel() + 1.0); + minValue = 0; + maxValue = log10(m_model->getMaximumLevel() + 1.0); + } else if (m_normalization == ColumnNormalization::None) { + minValue = m_model->getMinimumLevel(); + maxValue = m_model->getMaximumLevel(); } - if (cparams.maxValue <= cparams.minValue) { - cparams.maxValue = cparams.minValue + 0.1; + SVDEBUG << "Colour3DPlotLayer: rebuilding renderer, value range is " + << minValue << " -> " << maxValue << endl; + + if (maxValue <= minValue) { + maxValue = minValue + 0.1f; } + + cparams.threshold = minValue; + cparams.minValue = minValue; + cparams.maxValue = maxValue; + m_lastRenderedMags[viewId] = MagnitudeRange(float(minValue), + float(maxValue)); + Colour3DPlotRenderer::Parameters params; params.colourScale = ColourScale(cparams); params.normalization = m_normalization; @@ -1055,10 +1048,10 @@ params.invertVertical = m_invertVertical; params.interpolate = m_smooth; - m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params); + m_renderers[viewId] = new Colour3DPlotRenderer(sources, params); } - return m_renderers[v->getId()]; + return m_renderers[viewId]; } void @@ -1071,7 +1064,9 @@ MagnitudeRange magRange; int viewId = v->getId(); - if (!renderer->geometryChanged(v)) { + bool continuingPaint = !renderer->geometryChanged(v); + + if (continuingPaint) { magRange = m_viewMags[viewId]; } @@ -1092,18 +1087,25 @@ magRange.sample(result.range); if (magRange.isSet()) { - if (!(m_viewMags[viewId] == magRange)) { + if (m_viewMags[viewId] != magRange) { m_viewMags[viewId] = magRange; - //!!! now need to do the normalise-visible thing +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "mag range in this view has changed: " + << magRange.getMin() << " -> " << magRange.getMax() << endl; +#endif } } - cerr << "mag range in this view: " - << m_viewMags[v->getId()].getMin() - << " -> " - << m_viewMags[v->getId()].getMax() - << endl; - + if (!continuingPaint && m_normalizeVisibleArea && + m_viewMags[viewId] != m_lastRenderedMags[viewId]) { +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "mag range has changed from last rendered range: re-rendering" + << endl; +#endif + delete m_renderers[viewId]; + m_renderers.erase(viewId); + v->updatePaintRect(v->getPaintRect()); + } } void @@ -1121,11 +1123,11 @@ int completion = 0; if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) { - if (completion > 0) { - paint.fillRect(0, 10, v->getPaintWidth() * completion / 100, - 10, QColor(120, 120, 120)); - } - return; + if (completion > 0) { + paint.fillRect(0, 10, v->getPaintWidth() * completion / 100, + 10, QColor(120, 120, 120)); + } + return; } if (m_model->getWidth() == 0) { @@ -1136,26 +1138,16 @@ return; } - //!!!??? - - if (m_normalizeVisibleArea) { - rect = v->getPaintRect(); - } - - //!!! why is the setLayerDormant(false) found here in - //!!! SpectrogramLayer not present in Colour3DPlotLayer? - //!!! unnecessary? vestigial? forgotten? - paintWithRenderer(v, paint, rect); } bool Colour3DPlotLayer::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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); @@ -1167,9 +1159,9 @@ case SnapRight: frame = right; break; case SnapNearest: case SnapNeighbouring: - if (frame - left > right - frame) frame = right; - else frame = left; - break; + if (frame - left > right - frame) frame = right; + else frame = left; + break; } return true; @@ -1185,7 +1177,7 @@ "maxY=\"%4\" " "invertVertical=\"%5\" " "opaque=\"%6\" %7") - .arg(convertFromColourScale(m_colourScale)) + .arg(convertFromColourScale(m_colourScale)) .arg(m_colourMap) .arg(m_miny) .arg(m_maxy) @@ -1202,13 +1194,13 @@ // area as well afterwards s += QString("columnNormalization=\"%1\" ") - .arg(m_normalization == ColumnNormalization::Max1 ? "peak" : + .arg(m_normalization == ColumnNormalization::Range01 ? "peak" : m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none"); // Old-style normalization attribute, for backward compatibility s += QString("normalizeColumns=\"%1\" ") - .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false"); + .arg(m_normalization == ColumnNormalization::Range01 ? "true" : "false"); // And this applies to both old- and new-style attributes @@ -1224,14 +1216,14 @@ bool ok = false, alsoOk = false; ColourScaleType colourScale = convertToColourScale - (attributes.value("colourScale").toInt(&ok)); + (attributes.value("scale").toInt(&ok)); if (ok) setColourScale(colourScale); int colourMap = attributes.value("colourScheme").toInt(&ok); if (ok) setColourMap(colourMap); BinScale binScale = (BinScale) - attributes.value("binScale").toInt(&ok); + attributes.value("binScale").toInt(&ok); if (ok) setBinScale(binScale); bool invertVertical = @@ -1262,7 +1254,7 @@ haveNewStyleNormalization = true; if (columnNormalization == "peak") { - setNormalization(ColumnNormalization::Max1); + setNormalization(ColumnNormalization::Range01); } else if (columnNormalization == "hybrid") { setNormalization(ColumnNormalization::Hybrid); } else if (columnNormalization == "none") { @@ -1280,7 +1272,7 @@ bool normalizeColumns = (attributes.value("normalizeColumns").trimmed() == "true"); if (normalizeColumns) { - setNormalization(ColumnNormalization::Max1); + setNormalization(ColumnNormalization::Range01); } bool normalizeHybrid = diff -r 57d192e26331 -r 13d9b422f7fe layer/Colour3DPlotLayer.h --- a/layer/Colour3DPlotLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Colour3DPlotLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -59,8 +59,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); @@ -82,7 +82,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual QString getPropertyValueIconName(const PropertyName &, int value) const; virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; @@ -187,6 +187,8 @@ typedef std::map ViewMagMap; // key is view id mutable ViewMagMap m_viewMags; + mutable ViewMagMap m_lastRenderedMags; // when in normalizeVisibleArea mode + void invalidateMagnitudes(); typedef std::map ViewRendererMap; // key is view id mutable ViewRendererMap m_renderers; @@ -209,8 +211,6 @@ * if the vertical scale is the usual way up). */ double getBinForY(const LayerGeometryProvider *, double y) const; - - DenseThreeDimensionalModel::Column getColumn(int col) const; int getColourScaleWidth(QPainter &) const; diff -r 57d192e26331 -r 13d9b422f7fe layer/Colour3DPlotRenderer.cpp --- a/layer/Colour3DPlotRenderer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Colour3DPlotRenderer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -17,6 +17,7 @@ #include "RenderTimer.h" #include "base/Profiler.h" +#include "base/HitCount.h" #include "data/model/DenseThreeDimensionalModel.h" #include "data/model/Dense3DModelPeakCache.h" @@ -94,14 +95,31 @@ { RenderType renderType = decideRenderType(v); - if (renderType != DrawBufferPixelResolution) { - // Rendering should be fast in bin-resolution and direct draw - // cases because we are quite well zoomed-in, and the sums are - // easier this way. Calculating boundaries later will be - // fiddly for partial paints otherwise. - timeConstrained = false; + if (timeConstrained) { + if (renderType != DrawBufferPixelResolution) { + // Rendering should be fast in bin-resolution and direct + // draw cases because we are quite well zoomed-in, and the + // sums are easier this way. Calculating boundaries later + // will be fiddly for partial paints otherwise. + timeConstrained = false; + + } else if (m_secondsPerXPixelValid) { + double predicted = m_secondsPerXPixel * rect.width(); +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "Predicted time for width " << rect.width() << " = " + << predicted << " (" << m_secondsPerXPixel << " x " + << rect.width() << ")" << endl; +#endif + if (predicted < 0.2) { +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "Predicted time looks fast enough: no partial renders" + << endl; +#endif + timeConstrained = false; + } + } } - + int x0 = v->getXForViewX(rect.x()); int x1 = v->getXForViewX(rect.x() + rect.width()); if (x0 < 0) x0 = 0; @@ -121,15 +139,17 @@ } #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "cache start " << m_cache.getStartFrame() + SVDEBUG << "cache start " << m_cache.getStartFrame() << " valid left " << m_cache.getValidLeft() << " valid right " << m_cache.getValidRight() << endl; - cerr << " view start " << startFrame + SVDEBUG << " view start " << startFrame << " x0 " << x0 << " x1 " << x1 << endl; #endif + + static HitCount count("Colour3DPlotRenderer: image cache"); if (m_cache.isValid()) { // some part of the cache is valid @@ -139,8 +159,9 @@ m_cache.getValidRight() >= x1) { #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "cache hit" << endl; + SVDEBUG << "cache hit" << endl; #endif + count.hit(); // cache is valid for the complete requested area paint.drawImage(rect, m_cache.getImage(), rect); @@ -151,8 +172,9 @@ } else { #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "cache partial hit" << endl; + SVDEBUG << "cache partial hit" << endl; #endif + count.partial(); // cache doesn't begin at the right frame or doesn't // contain the complete view, but might be scrollable or @@ -175,6 +197,7 @@ } } else { // cache is completely invalid + count.miss(); m_cache.setStartFrame(startFrame); m_magCache.setStartFrame(startFrame); } @@ -185,14 +208,50 @@ int reqx1 = x1; if (!m_cache.isValid() && timeConstrained) { - // When rendering the whole area, in a context where we might - // not be able to complete the work, start from somewhere near - // the middle so that the region of interest appears first + if (x0 == 0 && x1 == v->getPaintWidth()) { + + // When rendering the whole area, in a context where we + // might not be able to complete the work, start from + // somewhere near the middle so that the region of + // interest appears first. + // + // This is very useful if we actually are slow to render, + // but if we're not sure how fast we'll be, we should + // prefer not to because it can be distracting to render + // fast from the middle and then jump back to fill in the + // start. That is: + // + // - if our seconds-per-x-pixel count is invalid, then we + // don't do this: we've probably only just been created + // and don't know how fast we'll be yet (this happens + // often while zooming rapidly in and out). The exception + // to the exception is if we're displaying peak + // frequencies; this we can assume to be slow. (Note that + // if the seconds-per-x-pixel is valid and we know we're + // fast, then we've already set timeConstrained false + // above so this doesn't apply) + // + // - if we're using a peak cache, we don't do this; + // drawing from peak cache is often (even if not always) + // fast. - //!!! (perhaps we should avoid doing this if past repaints - //!!! have been fast enough to do the whole in one shot) - if (x0 == 0 && x1 == v->getPaintWidth()) { - x0 = int(x1 * 0.3); + bool drawFromTheMiddle = true; + + if (!m_secondsPerXPixelValid && + (m_params.binDisplay != BinDisplay::PeakFrequencies)) { + drawFromTheMiddle = false; + } else { + int peakCacheIndex = -1, binsPerPeak = -1; + getPreferredPeakCache(v, peakCacheIndex, binsPerPeak); + if (peakCacheIndex >= 0) { // have a peak cache + drawFromTheMiddle = false; + } + } + + if (drawFromTheMiddle) { + double offset = 0.5 * (double(rand()) / double(RAND_MAX)); + x0 = int(x1 * offset); + } } } @@ -238,7 +297,7 @@ pr.x(), pr.y(), pr.width(), pr.height()); if (!timeConstrained && (pr != rect)) { - cerr << "WARNING: failed to render entire requested rect " + SVCERR << "WARNING: failed to render entire requested rect " << "even when not time-constrained" << endl; } @@ -287,7 +346,7 @@ ColumnOp::Column Colour3DPlotRenderer::getColumn(int sx, int minbin, int nbins, - bool usePeakCache) const + int peakCacheIndex) const { Profiler profiler("Colour3DPlotRenderer::getColumn"); @@ -309,10 +368,12 @@ fullColumn.data() + minbin + nbins); } else { - + ColumnOp::Column fullColumn = - (usePeakCache ? m_sources.peakCache : m_sources.source)-> - getColumn(sx); + (peakCacheIndex >= 0 ? + m_sources.peakCaches[peakCacheIndex] : + m_sources.source) + ->getColumn(sx); column = vector(fullColumn.data() + minbin, fullColumn.data() + minbin + nbins); @@ -389,7 +450,7 @@ // peak pick -> distribute/interpolate -> apply display gain // this does the first three: - preparedColumn = getColumn(sx, minbin, nbins, false); + preparedColumn = getColumn(sx, minbin, nbins, -1); magRange.sample(preparedColumn); @@ -403,21 +464,21 @@ psx = sx; } - sv_frame_t fx = sx * modelResolution + modelStart; + sv_frame_t fx = sx * modelResolution + modelStart; - if (fx + modelResolution <= modelStart || fx > modelEnd) continue; + if (fx + modelResolution <= modelStart || fx > modelEnd) continue; int rx0 = v->getXForFrame(int(double(fx) * rateRatio)); - int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio)); + int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio)); - int rw = rx1 - rx0; - if (rw < 1) rw = 1; + int rw = rx1 - rx0; + if (rw < 1) rw = 1; - bool showLabel = (rw > 10 && - paint.fontMetrics().width("0.000000") < rw - 3 && - paint.fontMetrics().height() < (h / sh)); + bool showLabel = (rw > 10 && + paint.fontMetrics().width("0.000000") < rw - 3 && + paint.fontMetrics().height() < (h / sh)); - for (int sy = minbin; sy < minbin + nbins; ++sy) { + for (int sy = minbin; sy < minbin + nbins; ++sy) { int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy); int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1); @@ -440,30 +501,30 @@ continue; } - QColor pen(255, 255, 255, 80); - QColor brush(colour); + QColor pen(255, 255, 255, 80); + QColor brush(colour); if (rw > 3 && r.height() > 3) { brush.setAlpha(160); } - paint.setPen(Qt::NoPen); - paint.setBrush(brush); + paint.setPen(Qt::NoPen); + paint.setBrush(brush); - if (illuminate) { - if (r.contains(illuminatePos)) { - paint.setPen(v->getForeground()); - } - } + if (illuminate) { + if (r.contains(illuminatePos)) { + paint.setPen(v->getForeground()); + } + } -#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT -// cerr << "rect " << r.x() << "," << r.y() << " " +#ifdef DEBUG_COLOUR_PLOT_REPAINT +// SVDEBUG << "rect " << r.x() << "," << r.y() << " " // << r.width() << "x" << r.height() << endl; #endif - paint.drawRect(r); + paint.drawRect(r); - if (showLabel) { + if (showLabel) { double value = model->getValueAt(sx, sy); snprintf(labelbuf, buflen, "%06f", value); QString text(labelbuf); @@ -474,14 +535,53 @@ ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(), text, PaintAssistant::OutlinedText); - } - } + } + } } return magRange; } void +Colour3DPlotRenderer::getPreferredPeakCache(const LayerGeometryProvider *v, + int &peakCacheIndex, + int &binsPerPeak) const +{ + peakCacheIndex = -1; + binsPerPeak = -1; + + const DenseThreeDimensionalModel *model = m_sources.source; + if (!model) return; + if (m_params.binDisplay == BinDisplay::PeakFrequencies) return; + if (m_params.colourScale.getScale() == ColourScaleType::Phase) return; + + int zoomLevel = v->getZoomLevel(); + int binResolution = model->getResolution(); + + for (int ix = 0; in_range_for(m_sources.peakCaches, ix); ++ix) { + int bpp = m_sources.peakCaches[ix]->getColumnsPerPeak(); + int equivZoom = binResolution * bpp; + if (zoomLevel >= equivZoom) { + // this peak cache would work, though it might not be best + if (bpp > binsPerPeak) { + // ok, it's better than the best one we've found so far + peakCacheIndex = ix; + binsPerPeak = bpp; + } + } + } + +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "getPreferredPeakCache: zoomLevel = " << zoomLevel + << ", binResolution " << binResolution + << ", binsPerPeak " << binsPerPeak + << ", peakCacheIndex " << peakCacheIndex + << ", peakCaches " << m_sources.peakCaches.size() + << endl; +#endif +} + +void Colour3DPlotRenderer::renderToCachePixelResolution(const LayerGeometryProvider *v, int x0, int repaintWidth, bool rightToLeft, @@ -489,7 +589,7 @@ { Profiler profiler("Colour3DPlotRenderer::renderToCachePixelResolution"); #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "renderToCachePixelResolution" << endl; + SVDEBUG << "renderToCachePixelResolution" << endl; #endif // Draw to the draw buffer, and then copy from there. The draw @@ -498,7 +598,7 @@ const DenseThreeDimensionalModel *model = m_sources.source; if (!model || !model->isOK() || !model->isReady()) { - throw std::logic_error("no source model provided, or model not ready"); + throw std::logic_error("no source model provided, or model not ready"); } int h = v->getPaintHeight(); @@ -508,9 +608,6 @@ vector binforx(repaintWidth); vector binfory(h); - bool usePeakCache = false; - int binsPerPeak = 1; - int zoomLevel = v->getZoomLevel(); int binResolution = model->getResolution(); for (int x = 0; x < repaintWidth; ++x) { @@ -519,21 +616,10 @@ binforx[x] = int(s0 + 0.0001); } - if (m_sources.peakCache) { - binsPerPeak = m_sources.peakCache->getColumnsPerPeak(); - usePeakCache = (zoomLevel >= binResolution * binsPerPeak); - if (m_params.colourScale.getScale() == - ColourScaleType::Phase) { - usePeakCache = false; - } - } + int peakCacheIndex = -1; + int binsPerPeak = -1; - SVDEBUG << "[PIX] zoomLevel = " << zoomLevel - << ", binResolution " << binResolution - << ", binsPerPeak " << binsPerPeak - << ", peak cache " << m_sources.peakCache - << ", usePeakCache = " << usePeakCache - << endl; + getPreferredPeakCache(v, peakCacheIndex, binsPerPeak); for (int y = 0; y < h; ++y) { binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); @@ -555,7 +641,7 @@ h, binforx, binfory, - usePeakCache, + peakCacheIndex, rightToLeft, timeConstrained); } @@ -644,7 +730,7 @@ { Profiler profiler("Colour3DPlotRenderer::renderToCacheBinResolution"); #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "renderToCacheBinResolution" << endl; + SVDEBUG << "renderToCacheBinResolution" << endl; #endif // Draw to the draw buffer, and then scale-copy from there. Draw @@ -653,7 +739,7 @@ const DenseThreeDimensionalModel *model = m_sources.source; if (!model || !model->isOK() || !model->isReady()) { - throw std::logic_error("no source model provided, or model not ready"); + throw std::logic_error("no source model provided, or model not ready"); } // The draw buffer will contain a fragment at bin resolution. We @@ -710,7 +796,9 @@ binforx[x] = int(leftBoundaryFrame / binResolution) + x; } +#ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "[BIN] binResolution " << binResolution << endl; +#endif for (int y = 0; y < h; ++y) { binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); @@ -720,7 +808,7 @@ h, binforx, binfory, - false, + -1, false, false); @@ -730,9 +818,9 @@ int scaledRight = v->getXForFrame(rightBoundaryFrame); #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "scaling draw buffer from width " << m_drawBuffer.width() - << " to " << (scaledRight - scaledLeft) << " (nb drawBufferWidth = " - << drawBufferWidth << ")" << endl; + SVDEBUG << "scaling draw buffer from width " << m_drawBuffer.width() + << " to " << (scaledRight - scaledLeft) << " (nb drawBufferWidth = " + << drawBufferWidth << ")" << endl; #endif QImage scaled = scaleDrawBufferImage @@ -757,8 +845,8 @@ } #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "repaintWidth = " << repaintWidth - << ", targetWidth = " << targetWidth << endl; + SVDEBUG << "repaintWidth = " << repaintWidth + << ", targetWidth = " << targetWidth << endl; #endif if (targetWidth > 0) { @@ -783,13 +871,13 @@ Colour3DPlotRenderer::renderDrawBuffer(int w, int h, const vector &binforx, const vector &binfory, - bool usePeakCache, + int peakCacheIndex, bool rightToLeft, bool timeConstrained) { // Callers must have checked that the appropriate subset of // Sources data members are set for the supplied flags (e.g. that - // peakCache model exists if usePeakCache) + // peakCache corresponding to peakCacheIndex exists) RenderTimer timer(timeConstrained ? RenderTimer::FastRender : @@ -799,13 +887,14 @@ int divisor = 1; const DenseThreeDimensionalModel *sourceModel = m_sources.source; - if (usePeakCache) { - divisor = m_sources.peakCache->getColumnsPerPeak(); - sourceModel = m_sources.peakCache; + if (peakCacheIndex >= 0) { + divisor = m_sources.peakCaches[peakCacheIndex]->getColumnsPerPeak(); + sourceModel = m_sources.peakCaches[peakCacheIndex]; } +#ifdef DEBUG_COLOUR_PLOT_REPAINT SVDEBUG << "renderDrawBuffer: w = " << w << ", h = " << h - << ", usePeakCache = " << usePeakCache << " (divisor = " + << ", peakCacheIndex = " << peakCacheIndex << " (divisor = " << divisor << "), rightToLeft = " << rightToLeft << ", timeConstrained = " << timeConstrained << endl; SVDEBUG << "renderDrawBuffer: normalization = " << int(m_params.normalization) @@ -813,6 +902,7 @@ << ", binScale = " << int(m_params.binScale) << ", alwaysOpaque = " << m_params.alwaysOpaque << ", interpolate = " << m_params.interpolate << endl; +#endif int sh = sourceModel->getHeight(); @@ -824,7 +914,7 @@ if (minbin + nbins > sh) nbins = sh - minbin; #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "minbin = " << minbin << ", nbins = " << nbins << ", last binfory = " + SVDEBUG << "minbin = " << minbin << ", nbins = " << nbins << ", last binfory = " << binfory[h-1] << " (rounds to " << int(binfory[h-1]) << ") (model height " << sh << ")" << endl; #endif @@ -840,14 +930,14 @@ step = -1; } - int columnCount = 0; + int xPixelCount = 0; vector preparedColumn; int modelWidth = sourceModel->getWidth(); #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "modelWidth " << modelWidth << ", divisor " << divisor << endl; + SVDEBUG << "modelWidth " << modelWidth << ", divisor " << divisor << endl; #endif for (int x = start; x != finish; x += step) { @@ -855,7 +945,7 @@ // x is the on-canvas pixel coord; sx (later) will be the // source column index - ++columnCount; + ++xPixelCount; if (binforx[x] < 0) continue; @@ -867,7 +957,7 @@ if (sx1 <= sx0) sx1 = sx0 + 1; #ifdef DEBUG_COLOUR_PLOT_REPAINT -// cerr << "x = " << x << ", binforx[x] = " << binforx[x] << ", sx range " << sx0 << " -> " << sx1 << endl; +// SVDEBUG << "x = " << x << ", binforx[x] = " << binforx[x] << ", sx range " << sx0 << " -> " << sx1 << endl; #endif vector pixelPeakColumn; @@ -887,7 +977,7 @@ // this does the first three: ColumnOp::Column column = getColumn(sx, minbin, nbins, - usePeakCache); + peakCacheIndex); magRange.sample(column); @@ -936,14 +1026,18 @@ m_magRanges.push_back(magRange); } - double fractionComplete = double(columnCount) / double(w); + double fractionComplete = double(xPixelCount) / double(w); if (timer.outOfTime(fractionComplete)) { - cerr << "out of time" << endl; - return columnCount; +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "out of time" << endl; +#endif + updateTimings(timer, xPixelCount); + return xPixelCount; } } - return columnCount; + updateTimings(timer, xPixelCount); + return xPixelCount; } int @@ -959,7 +1053,7 @@ // fft model exists) RenderTimer timer(timeConstrained ? - RenderTimer::FastRender : + RenderTimer::SlowRender : RenderTimer::NoTimeout); const FFTModel *fft = m_sources.fft; @@ -987,13 +1081,13 @@ step = -1; } - int columnCount = 0; + int xPixelCount = 0; vector preparedColumn; int modelWidth = fft->getWidth(); #ifdef DEBUG_COLOUR_PLOT_REPAINT - cerr << "modelWidth " << modelWidth << endl; + SVDEBUG << "modelWidth " << modelWidth << endl; #endif double minFreq = @@ -1002,13 +1096,18 @@ (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize(); bool logarithmic = (m_params.binScale == BinScale::Log); + +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "start = " << start << ", finish = " << finish + << ", step = " << step << endl; +#endif for (int x = start; x != finish; x += step) { // x is the on-canvas pixel coord; sx (later) will be the // source column index - ++columnCount; + ++xPixelCount; if (binforx[x] < 0) continue; @@ -1029,7 +1128,7 @@ } if (sx != psx) { - preparedColumn = getColumn(sx, minbin, nbins, false); + preparedColumn = getColumn(sx, minbin, nbins, -1); magRange.sample(preparedColumn); psx = sx; } @@ -1048,6 +1147,11 @@ if (!pixelPeakColumn.empty()) { +#ifdef DEBUG_COLOUR_PLOT_REPAINT +// SVDEBUG << "found " << peakfreqs.size() << " peak freqs at column " +// << sx0 << endl; +#endif + for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin(); pi != peakfreqs.end(); ++pi) { @@ -1065,22 +1169,58 @@ int iy = int(y + 0.5); if (iy < 0 || iy >= h) continue; - m_drawBuffer.setPixel - (x, - iy, - m_params.colourScale.getPixel(value)); + auto pixel = m_params.colourScale.getPixel(value); + +#ifdef DEBUG_COLOUR_PLOT_REPAINT +// SVDEBUG << "frequency " << freq << " for bin " << bin +// << " -> y = " << y << ", iy = " << iy << ", value = " +// << value << ", pixel " << pixel << "\n"; +#endif + + m_drawBuffer.setPixel(x, iy, pixel); } m_magRanges.push_back(magRange); + + } else { +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "pixel peak column for range " << sx0 << " to " << sx1 + << " is empty" << endl; +#endif } - double fractionComplete = double(columnCount) / double(w); + double fractionComplete = double(xPixelCount) / double(w); if (timer.outOfTime(fractionComplete)) { - return columnCount; +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "out of time" << endl; +#endif + updateTimings(timer, xPixelCount); + return xPixelCount; } } - return columnCount; + updateTimings(timer, xPixelCount); + return xPixelCount; +} + +void +Colour3DPlotRenderer::updateTimings(const RenderTimer &timer, int xPixelCount) +{ + double secondsPerXPixel = timer.secondsPerItem(xPixelCount); + + // valid if we have enough data points, or if the overall time is + // massively slow anyway (as we definitely need to warn about that) + bool valid = (xPixelCount > 20 || secondsPerXPixel > 0.01); + + if (valid) { + m_secondsPerXPixel = secondsPerXPixel; + m_secondsPerXPixelValid = true; + +#ifdef DEBUG_COLOUR_PLOT_REPAINT + SVDEBUG << "across " << xPixelCount << " x-pixels, seconds per x-pixel = " + << m_secondsPerXPixel << endl; +#endif + } } void diff -r 57d192e26331 -r 13d9b422f7fe layer/Colour3DPlotRenderer.h --- a/layer/Colour3DPlotRenderer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Colour3DPlotRenderer.h Mon Sep 17 13:51:31 2018 +0100 @@ -32,6 +32,7 @@ class DenseThreeDimensionalModel; class Dense3DModelPeakCache; class FFTModel; +class RenderTimer; enum class BinDisplay { AllBins, @@ -48,22 +49,22 @@ { public: struct Sources { - Sources() : verticalBinLayer(0), source(0), peakCache(0), fft(0) { } + Sources() : verticalBinLayer(0), source(0), fft(0) { } // These must all outlive this class const VerticalBinLayer *verticalBinLayer; // always - const DenseThreeDimensionalModel *source; // always - const Dense3DModelPeakCache *peakCache; // optionally - const FFTModel *fft; // optionally + const DenseThreeDimensionalModel *source; // always + const FFTModel *fft; // optionally + std::vector peakCaches; // zero or more }; struct Parameters { - Parameters() : - colourScale(ColourScale::Parameters()), - normalization(ColumnNormalization::None), - binDisplay(BinDisplay::AllBins), + Parameters() : + colourScale(ColourScale::Parameters()), + normalization(ColumnNormalization::None), + binDisplay(BinDisplay::AllBins), binScale(BinScale::Linear), - alwaysOpaque(false), + alwaysOpaque(false), interpolate(false), invertVertical(false), scaleFactor(1.0), @@ -72,32 +73,32 @@ /** A complete ColourScale object by value, used for colour * map conversion. Note that the final display gain setting is * also encapsulated here. */ - ColourScale colourScale; + ColourScale colourScale; /** Type of column normalization. */ - ColumnNormalization normalization; + ColumnNormalization normalization; /** Selection of bins to display. */ - BinDisplay binDisplay; + BinDisplay binDisplay; /** Scale for vertical bin spacing (linear or logarithmic). */ - BinScale binScale; + BinScale binScale; /** Whether cells should always be opaque. If false, then * large cells (when zoomed in a long way) will be rendered * translucent in order not to obscure anything in a layer * beneath. */ - bool alwaysOpaque; + bool alwaysOpaque; /** Whether to apply smoothing when rendering cells at more * than one pixel per cell. !!! todo: decide about separating * out x-interpolate and y-interpolate as the spectrogram * actually does (or used to) */ - bool interpolate; + bool interpolate; /** Whether to render the whole caboodle upside-down. */ - bool invertVertical; + bool invertVertical; /** Initial scale factor (e.g. for FFT scaling). This factor * is applied to all values read from the underlying model @@ -111,7 +112,9 @@ Colour3DPlotRenderer(Sources sources, Parameters parameters) : m_sources(sources), - m_params(parameters) + m_params(parameters), + m_secondsPerXPixel(0.0), + m_secondsPerXPixelValid(false) { } struct RenderResult { @@ -123,7 +126,13 @@ QRect rendered; /** - * The magnitude range of the data in the rendered area. + * The magnitude range of the data in the rendered area, after + * initial scaling (parameters.scaleFactor) and normalisation, + * for use in displaying colour scale etc. (Note that the + * magnitude range *before* normalisation would not be very + * meaningful for this purpose, as the scale would need to be + * different for every column if column or hybrid + * normalisation was in use.) */ MagnitudeRange range; }; @@ -263,6 +272,9 @@ // valid range in the magnitude cache, but not necessarily vice // versa (as the image cache is limited to contiguous ranges). ScrollableMagRangeCache m_magCache; + + double m_secondsPerXPixel; + bool m_secondsPerXPixelValid; RenderResult render(const LayerGeometryProvider *v, QPainter &paint, QRect rect, bool timeConstrained); @@ -280,7 +292,7 @@ int renderDrawBuffer(int w, int h, const std::vector &binforx, const std::vector &binfory, - bool usePeakCache, + int peakCacheIndex, // -1 => don't use a peak cache bool rightToLeft, bool timeConstrained); @@ -306,7 +318,12 @@ const; ColumnOp::Column getColumn(int sx, int minbin, int nbins, - bool usePeakCache) const; + int peakCacheIndex) const; // -1 => don't use cache + + void getPreferredPeakCache(const LayerGeometryProvider *, + int &peakCacheIndex, int &binsPerPeak) const; + + void updateTimings(const RenderTimer &timer, int xPixelCount); }; #endif diff -r 57d192e26331 -r 13d9b422f7fe layer/ColourDatabase.cpp --- a/layer/ColourDatabase.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ColourDatabase.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -160,7 +160,7 @@ { int index = -1; if (colourSpec != "") { - QColor colour(colourSpec); + QColor colour(colourSpec); index = getColourIndex(colour); if (index < 0) { index = addColour(colour, diff -r 57d192e26331 -r 13d9b422f7fe layer/ColourMapper.cpp --- a/layer/ColourMapper.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ColourMapper.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2007 Chris Cannam and QMUL. + This file copyright 2006-2016 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -23,6 +23,8 @@ #include +#include + using namespace std; static vector convertStrings(const vector &strs) @@ -65,7 +67,7 @@ m_max(max) { if (m_min == m_max) { - cerr << "WARNING: ColourMapper: min == max (== " << m_min + SVCERR << "WARNING: ColourMapper: min == max (== " << m_min << "), adjusting" << endl; m_max = m_min + 1; } @@ -320,4 +322,29 @@ } } +QPixmap +ColourMapper::getExamplePixmap(QSize size) const +{ + QPixmap pmap(size); + pmap.fill(Qt::white); + QPainter paint(&pmap); + int w = size.width(), h = size.height(); + + int margin = 2; + if (w < 4 || h < 4) margin = 0; + else if (w < 8 || h < 8) margin = 1; + + int n = w - margin*2; + + for (int x = 0; x < n; ++x) { + double value = m_min + ((m_max - m_min) * x) / (n-1); + QColor colour(map(value)); + paint.setPen(colour); + paint.drawLine(x + margin, margin, x + margin, h - margin); + } + + return pmap; +} + + diff -r 57d192e26331 -r 13d9b422f7fe layer/ColourMapper.h --- a/layer/ColourMapper.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ColourMapper.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,12 +13,13 @@ COPYING included with this distribution for more information. */ -#ifndef _COLOUR_MAPPER_H_ -#define _COLOUR_MAPPER_H_ +#ifndef SV_COLOUR_MAPPER_H +#define SV_COLOUR_MAPPER_H #include #include #include +#include /** * A class for mapping intensity values onto various colour maps. @@ -59,6 +60,8 @@ QColor getContrastingColour() const; // for cursors etc bool hasLightBackground() const; + QPixmap getExamplePixmap(QSize size) const; + protected: int m_map; double m_min; diff -r 57d192e26331 -r 13d9b422f7fe layer/ColourScale.cpp --- a/layer/ColourScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ColourScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -30,9 +30,9 @@ m_mapper(m_params.colourMap, 1.f, double(m_maxPixel)) { if (m_params.minValue >= m_params.maxValue) { - cerr << "ERROR: ColourScale::ColourScale: minValue = " + SVCERR << "ERROR: ColourScale::ColourScale: minValue = " << m_params.minValue << ", maxValue = " << m_params.maxValue << endl; - throw std::logic_error("maxValue must be greater than minValue"); + throw std::logic_error("maxValue must be greater than minValue"); } m_mappedMin = m_params.minValue; @@ -44,30 +44,47 @@ if (m_params.scaleType == ColourScaleType::Log) { - LogRange::mapRange(m_mappedMin, m_mappedMax); - + // When used in e.g. spectrogram, we have a range with a min + // value of zero. The LogRange converts that to a threshold + // value of -10, so for a range of e.g. (0,1) we end up with + // (-10,0) as the mapped range. + // + // But in other contexts we could end up with a mapped range + // much larger than that if we have a small non-zero minimum + // value (less than 1e-10), or a particularly large + // maximum. That's unlikely to give us good results, so let's + // insist that the mapped log range has no more than 10 + // difference between min and max, to match the behaviour when + // min == 0 at the input. + // + double threshold = -10.0; + LogRange::mapRange(m_mappedMin, m_mappedMax, threshold); + if (m_mappedMin < m_mappedMax + threshold) { + m_mappedMin = m_mappedMax + threshold; + } + } else if (m_params.scaleType == ColourScaleType::PlusMinusOne) { - - m_mappedMin = -1.0; - m_mappedMax = 1.0; + + m_mappedMin = -1.0; + m_mappedMax = 1.0; } else if (m_params.scaleType == ColourScaleType::Absolute) { - m_mappedMin = fabs(m_mappedMin); - m_mappedMax = fabs(m_mappedMax); - if (m_mappedMin >= m_mappedMax) { - std::swap(m_mappedMin, m_mappedMax); - } + m_mappedMin = fabs(m_mappedMin); + m_mappedMax = fabs(m_mappedMax); + if (m_mappedMin >= m_mappedMax) { + std::swap(m_mappedMin, m_mappedMax); + } } if (m_mappedMin >= m_mappedMax) { - cerr << "ERROR: ColourScale::ColourScale: minValue = " << m_params.minValue + SVCERR << "ERROR: ColourScale::ColourScale: minValue = " << m_params.minValue << ", maxValue = " << m_params.maxValue << ", threshold = " << m_params.threshold << ", scale = " << int(m_params.scaleType) << " resulting in mapped minValue = " << m_mappedMin << ", mapped maxValue = " << m_mappedMax << endl; - throw std::logic_error("maxValue must be greater than minValue [after mapping]"); + throw std::logic_error("maxValue must be greater than minValue [after mapping]"); } } @@ -87,9 +104,9 @@ double maxPixF = m_maxPixel; if (m_params.scaleType == ColourScaleType::Phase) { - double half = (maxPixF - 1.f) / 2.f; + double half = (maxPixF - 1.f) / 2.f; int pixel = 1 + int((value * half) / M_PI + half); -// cerr << "phase = " << value << " pixel = " << pixel << endl; +// SVCERR << "phase = " << value << " pixel = " << pixel << endl; return pixel; } @@ -100,21 +117,21 @@ double mapped = value; if (m_params.scaleType == ColourScaleType::Log) { - mapped = LogRange::map(value); + mapped = LogRange::map(value); } else if (m_params.scaleType == ColourScaleType::PlusMinusOne) { - if (mapped < -1.f) mapped = -1.f; - if (mapped > 1.f) mapped = 1.f; + if (mapped < -1.f) mapped = -1.f; + if (mapped > 1.f) mapped = 1.f; } else if (m_params.scaleType == ColourScaleType::Absolute) { - if (mapped < 0.f) mapped = -mapped; + if (mapped < 0.f) mapped = -mapped; } mapped *= m_params.multiple; if (mapped < m_mappedMin) { - mapped = m_mappedMin; + mapped = m_mappedMin; } if (mapped > m_mappedMax) { - mapped = m_mappedMax; + mapped = m_mappedMax; } double proportion = (mapped - m_mappedMin) / (m_mappedMax - m_mappedMin); @@ -122,16 +139,16 @@ int pixel = 0; if (m_params.scaleType == ColourScaleType::Meter) { - pixel = AudioLevel::multiplier_to_preview(proportion, m_maxPixel-1) + 1; + pixel = AudioLevel::multiplier_to_preview(proportion, m_maxPixel-1) + 1; } else { - pixel = int(proportion * maxPixF) + 1; + pixel = int(proportion * maxPixF) + 1; } if (pixel < 0) { - pixel = 0; + pixel = 0; } if (pixel > m_maxPixel) { - pixel = m_maxPixel; + pixel = m_maxPixel; } return pixel; } @@ -140,21 +157,21 @@ ColourScale::getColourForPixel(int pixel, int rotation) const { if (pixel < 0) { - pixel = 0; + pixel = 0; } if (pixel > m_maxPixel) { - pixel = m_maxPixel; + pixel = m_maxPixel; } if (pixel == 0) { - if (m_mapper.hasLightBackground()) { - return Qt::white; - } else { - return Qt::black; - } + if (m_mapper.hasLightBackground()) { + return Qt::white; + } else { + return Qt::black; + } } else { - int target = int(pixel) + rotation; - while (target < 1) target += m_maxPixel; - while (target > m_maxPixel) target -= m_maxPixel; - return m_mapper.map(double(target)); + int target = int(pixel) + rotation; + while (target < 1) target += m_maxPixel; + while (target > m_maxPixel) target -= m_maxPixel; + return m_mapper.map(double(target)); } } diff -r 57d192e26331 -r 13d9b422f7fe layer/ColourScale.h --- a/layer/ColourScale.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ColourScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -35,28 +35,28 @@ { public: struct Parameters { - Parameters() : colourMap(0), scaleType(ColourScaleType::Linear), - minValue(0.0), maxValue(1.0), - threshold(0.0), gain(1.0), multiple(1.0) { } + Parameters() : colourMap(0), scaleType(ColourScaleType::Linear), + minValue(0.0), maxValue(1.0), + threshold(0.0), gain(1.0), multiple(1.0) { } - /** A colour map index as used by ColourMapper */ - int colourMap; - - /** Distribution for the scale */ - ColourScaleType scaleType; - - /** Minimum value in source range */ - double minValue; - - /** Maximum value in source range. Must be > minValue */ - double maxValue; + /** A colour map index as used by ColourMapper */ + int colourMap; + + /** Distribution for the scale */ + ColourScaleType scaleType; + + /** Minimum value in source range */ + double minValue; + + /** Maximum value in source range. Must be > minValue */ + double maxValue; - /** Threshold below which every value is mapped to background - pixel 0 */ - double threshold; + /** Threshold below which every value is mapped to background + pixel 0 */ + double threshold; - /** Gain to apply before thresholding, mapping, and clamping */ - double gain; + /** Gain to apply before thresholding, mapping, and clamping */ + double gain; /** Multiple to apply after thresholding and mapping. In most * cases the gain parameter is the one you want instead of @@ -106,7 +106,7 @@ * equivalent to getColourForPixel(getPixel(value), rotation). */ QColor getColour(double value, int rotation) const { - return getColourForPixel(getPixel(value), rotation); + return getColourForPixel(getPixel(value), rotation); } private: diff -r 57d192e26331 -r 13d9b422f7fe layer/HorizontalFrequencyScale.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/HorizontalFrequencyScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,77 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "HorizontalFrequencyScale.h" +#include "HorizontalScaleProvider.h" +#include "LayerGeometryProvider.h" + +#include "base/ScaleTickIntervals.h" + +#include + +#include + +int +HorizontalFrequencyScale::getHeight(LayerGeometryProvider *, + QPainter &paint) +{ + return paint.fontMetrics().height() + 10; +} + +void +HorizontalFrequencyScale::paintScale(LayerGeometryProvider *v, + const HorizontalScaleProvider *p, + QPainter &paint, + QRect r, + bool logarithmic) +{ + int x0 = r.x(), y0 = r.y(), x1 = r.x() + r.width(), y1 = r.y() + r.height(); + + paint.drawLine(x0, y0, x1, y0); + + double f0 = p->getFrequencyForX(v, x0 ? x0 : 1); + double f1 = p->getFrequencyForX(v, x1); + + int n = 20; + + auto ticks = + logarithmic ? + ScaleTickIntervals::logarithmic({ f0, f1, n }) : + ScaleTickIntervals::linear({ f0, f1, n }); + + n = int(ticks.size()); + + int marginx = -1; + + for (int i = 0; i < n; ++i) { + + double val = ticks[i].value; + QString label = QString::fromStdString(ticks[i].label); + int tw = paint.fontMetrics().width(label); + + int x = int(round(p->getXForFrequency(v, val))); + + if (x < marginx) continue; + + //!!! todo: pixel scaling (here & elsewhere in these classes) + + paint.drawLine(x, y0, x, y1); + + paint.drawText(x + 5, y0 + paint.fontMetrics().ascent() + 5, label); + + marginx = x + tw + 10; + } +} + diff -r 57d192e26331 -r 13d9b422f7fe layer/HorizontalFrequencyScale.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/HorizontalFrequencyScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,35 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_HORIZONTAL_FREQUENCY_SCALE_H +#define SV_HORIZONTAL_FREQUENCY_SCALE_H + +#include + +class QPainter; +class LayerGeometryProvider; +class HorizontalScaleProvider; + +class HorizontalFrequencyScale +{ +public: + int getHeight(LayerGeometryProvider *v, QPainter &paint); + + void paintScale + (LayerGeometryProvider *v, const HorizontalScaleProvider *provider, + QPainter &paint, QRect r, bool logarithmic); +}; + +#endif diff -r 57d192e26331 -r 13d9b422f7fe layer/HorizontalScaleProvider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/HorizontalScaleProvider.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,38 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_HORIZONTAL_SCALE_PROVIDER_H +#define SV_HORIZONTAL_SCALE_PROVIDER_H + +class LayerGeometryProvider; + +/** + * Interface to be implemented by objects, such as layers or objects + * they delegate to, in which the X axis corresponds to frequency. For + * example, SpectrumLayer. + */ +class HorizontalScaleProvider +{ +public: + virtual double getFrequencyForX(const LayerGeometryProvider *, + double x) + const = 0; + + virtual double getXForFrequency(const LayerGeometryProvider *, + double freq) + const = 0; +}; + +#endif diff -r 57d192e26331 -r 13d9b422f7fe layer/ImageLayer.cpp --- a/layer/ImageLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ImageLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -91,14 +91,14 @@ int ImageLayer::getPropertyRangeAndValue(const PropertyName &name, - int *min, int *max, int *deflt) const + int *min, int *max, int *deflt) const { return Layer::getPropertyRangeAndValue(name, min, max, deflt); } QString ImageLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { return Layer::getPropertyValueLabel(name, value); } @@ -133,10 +133,10 @@ ImageModel::PointList rv; for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ) { + i != points.end(); ) { - const ImageModel::Point &p(*i); - int px = v->getXForFrame(p.frame); + const ImageModel::Point &p(*i); + int px = v->getXForFrame(p.frame); if (px > x) break; ++i; @@ -178,11 +178,11 @@ ImageModel::PointList points = getLocalPoints(v, x, pos.y()); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return ""; - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return ""; + } } // int useFrame = points.begin()->frame; @@ -192,14 +192,14 @@ QString text; /* if (points.begin()->label == "") { - 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); + 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); } pos = QPoint(v->getXForFrame(useFrame), - getYForHeight(v, points.begin()->height)); + getYForHeight(v, points.begin()->height)); */ return text; } @@ -209,22 +209,22 @@ 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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); ImageModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame), -1); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame), -1); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -232,47 +232,47 @@ bool found = false; for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } + if (i->frame > frame) { + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - ImageModel::PointList::const_iterator j = i; - ++j; + ImageModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -316,11 +316,11 @@ paint.setRenderHint(QPainter::Antialiasing, true); for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const ImageModel::Point &p(*i); + const ImageModel::Point &p(*i); - int x = v->getXForFrame(p.frame); + int x = v->getXForFrame(p.frame); int nx = x + 2000; ImageModel::PointList::const_iterator j = i; @@ -559,8 +559,8 @@ // SVDEBUG << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; if (!m_model) { - SVDEBUG << "ImageLayer::drawStart: no model" << endl; - return; + SVDEBUG << "ImageLayer::drawStart: no model" << endl; + return; } sv_frame_t frame = v->getFrameForX(e->x()); @@ -605,10 +605,10 @@ checkAddSource(dialog.getImage()); - ImageModel::ChangeImageCommand *command = - new ImageModel::ChangeImageCommand + ImageModel::ChangeImageCommand *command = + new ImageModel::ChangeImageCommand (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel()); - m_editingCommand->addCommand(command); + m_editingCommand->addCommand(command); } else { m_editingCommand->deletePoint(m_editingPoint); } @@ -652,8 +652,8 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -671,7 +671,7 @@ frame = (frame / m_model->getResolution()) * m_model->getResolution(); if (!m_editingCommand) { - m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image")); + m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image")); } m_editingCommand->deletePoint(m_editingPoint); @@ -686,7 +686,7 @@ if (!m_model || !m_editing) return; if (m_editingCommand) { - finish(m_editingCommand); + finish(m_editingCommand); } m_editingCommand = 0; @@ -712,8 +712,8 @@ checkAddSource(dialog.getImage()); - ImageModel::ChangeImageCommand *command = - new ImageModel::ChangeImageCommand + ImageModel::ChangeImageCommand *command = + new ImageModel::ChangeImageCommand (m_model, *points.begin(), dialog.getImage(), dialog.getLabel()); CommandHistory::getInstance()->addCommand(command); @@ -728,20 +728,20 @@ if (!m_model) return; ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Drag Selection")); + new ImageModel::EditCommand(m_model, tr("Drag Selection")); ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + 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); - } + if (s.contains(i->frame)) { + ImageModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -753,29 +753,29 @@ if (!m_model) return; ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Resize Selection")); + new ImageModel::EditCommand(m_model, tr("Resize Selection")); ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; + 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); - } + ImageModel::Point newPoint(*i); + newPoint.frame = lrint(target); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -787,14 +787,14 @@ if (!m_model) return; ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Delete Selection")); + new ImageModel::EditCommand(m_model, tr("Delete Selection")); ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + i != points.end(); ++i) { + if (s.contains(i->frame)) command->deletePoint(*i); } finish(command); @@ -806,11 +806,11 @@ if (!m_model) return; ImageModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (ImageModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -845,7 +845,7 @@ } ImageModel::EditCommand *command = - new ImageModel::EditCommand(m_model, tr("Paste")); + new ImageModel::EditCommand(m_model, tr("Paste")); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -925,7 +925,7 @@ const ImageModel::PointList &points(m_model->getPoints()); for (ImageModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { checkAddSource((*i).image); } diff -r 57d192e26331 -r 13d9b422f7fe layer/ImageLayer.h --- a/layer/ImageLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ImageLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -43,8 +43,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); @@ -73,7 +73,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); virtual ColourSignificance getLayerColourSignificance() const { diff -r 57d192e26331 -r 13d9b422f7fe layer/Layer.cpp --- a/layer/Layer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Layer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -52,10 +52,10 @@ this, SIGNAL(modelChanged())); connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t))); + this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t))); connect(model, SIGNAL(completionChanged()), - this, SIGNAL(modelCompletionChanged())); + this, SIGNAL(modelCompletionChanged())); connect(model, SIGNAL(alignmentCompletionChanged()), this, SIGNAL(modelAlignmentCompletionChanged())); @@ -65,7 +65,7 @@ Layer::getPropertyContainerIconName() const { return LayerFactory::getInstance()->getLayerIconName - (LayerFactory::getInstance()->getLayerType(this)); + (LayerFactory::getInstance()->getLayerType(this)); } void @@ -85,14 +85,14 @@ QString modelName; if (getModel()) modelName = getModel()->objectName(); - + QString text; if (modelName != "") { - text = QString("%1: %2").arg(modelName).arg(layerName); + text = QString("%1: %2").arg(modelName).arg(layerName); } else { - text = layerName; + text = layerName; } - + return text; } @@ -109,7 +109,7 @@ // cerr << "Layer (" << this << ", " << objectName() << ")::getPlayParameters: model is "<< getModel() << endl; const Model *model = getModel(); if (model) { - return PlayParameterRepository::getInstance()->getPlayParameters(model); + return PlayParameterRepository::getInstance()->getPlayParameters(model); } return 0; } @@ -588,6 +588,31 @@ v->drawMeasurementRect(paint, this, r.pixrect.normalized(), focus); } +bool +Layer::valueExtentsMatchMine(LayerGeometryProvider *v) const +{ + double min, min_; + double max, max_; + bool logarithmic, logarithmic_; + QString unit; + + if (!getValueExtents(min_, max_, logarithmic_, unit)) { + return false; + } + + if (!v->getValueExtents(unit, min, max, logarithmic)) { + return false; + } + + if (min != min_ || + max != max_ || + logarithmic != logarithmic_) { + return false; + } + + return true; +} + void Layer::toXml(QTextStream &stream, QString indent, QString extraAttributes) const @@ -600,12 +625,12 @@ } stream << QString("getLayerTypeName + .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName (LayerFactory::getInstance()->getLayerType(this)))) - .arg(getObjectExportId(this)) - .arg(encodeEntities(objectName())) - .arg(getObjectExportId(getModel())) - .arg(extraAttributes); + .arg(getObjectExportId(this)) + .arg(encodeEntities(objectName())) + .arg(getObjectExportId(getModel())) + .arg(extraAttributes); if (m_measureRects.empty()) { stream << QString("/>\n"); @@ -634,11 +659,11 @@ } stream << QString("\n") - .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName + .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName (LayerFactory::getInstance()->getLayerType(this)))) - .arg(getObjectExportId(this)) - .arg(encodeEntities(objectName())) - .arg(getObjectExportId(getModel())) + .arg(getObjectExportId(this)) + .arg(encodeEntities(objectName())) + .arg(getObjectExportId(getModel())) .arg(extraAttributes); } diff -r 57d192e26331 -r 13d9b422f7fe layer/Layer.h --- a/layer/Layer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/Layer.h Mon Sep 17 13:51:31 2018 +0100 @@ -51,7 +51,7 @@ */ class Layer : public PropertyContainer, - public XmlExportable + public XmlExportable { Q_OBJECT @@ -61,7 +61,7 @@ virtual const Model *getModel() const = 0; Model *getModel() { - return const_cast(const_cast(this)->getModel()); + return const_cast(const_cast(this)->getModel()); } /** @@ -106,13 +106,13 @@ virtual void setSynchronousPainting(bool /* synchronous */) { } enum VerticalPosition { - PositionTop, PositionMiddle, PositionBottom + PositionTop, PositionMiddle, PositionBottom }; virtual VerticalPosition getPreferredTimeRulerPosition() const { - return PositionMiddle; + return PositionMiddle; } virtual VerticalPosition getPreferredFrameCountPosition() const { - return PositionBottom; + return PositionBottom; } virtual bool hasLightBackground() const { return true; @@ -122,7 +122,7 @@ virtual QString getPropertyContainerName() const { if (m_presentationName != "") return m_presentationName; - else return objectName(); + else return objectName(); } virtual void setPresentationName(QString name); @@ -149,7 +149,7 @@ QPoint now) const; virtual QString getFeatureDescription(LayerGeometryProvider *, QPoint &) const { - return ""; + return ""; } virtual QString getLabelPreceding(sv_frame_t /* frame */) const { @@ -157,10 +157,10 @@ } enum SnapType { - SnapLeft, - SnapRight, - SnapNearest, - SnapNeighbouring + SnapLeft, + SnapRight, + SnapNearest, + SnapNeighbouring }; /** @@ -183,11 +183,11 @@ * the resolution of the model in this layer in sample frames. */ virtual bool snapToFeatureFrame(LayerGeometryProvider * /* v */, - sv_frame_t & /* frame */, - int &resolution, - SnapType /* snap */) const { - resolution = 1; - return false; + sv_frame_t & /* frame */, + int &resolution, + SnapType /* snap */) const { + resolution = 1; + return false; } /** @@ -210,8 +210,8 @@ sv_frame_t & /* source frame */, int &resolution, SnapType /* snap */) const { - resolution = 1; - return false; + resolution = 1; + return false; } // Draw, erase, and edit modes: @@ -626,6 +626,8 @@ void paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint, const MeasureRect &r, bool focus) const; + bool valueExtentsMatchMine(LayerGeometryProvider *v) const; + QString m_presentationName; private: diff -r 57d192e26331 -r 13d9b422f7fe layer/LayerFactory.cpp --- a/layer/LayerFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LayerFactory.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -84,14 +84,14 @@ case Slice: return Layer::tr("Time Slice"); case MelodicRangeSpectrogram: - // The user can change all the parameters of this after the - // fact -- there's nothing permanently melodic-range about it - // that should be encoded in its name - return Layer::tr("Spectrogram"); + // The user can change all the parameters of this after the + // fact -- there's nothing permanently melodic-range about it + // that should be encoded in its name + return Layer::tr("Spectrogram"); case PeakFrequencySpectrogram: - // likewise - return Layer::tr("Spectrogram"); + // likewise + return Layer::tr("Spectrogram"); case UnknownLayer: default: @@ -139,47 +139,47 @@ LayerTypeSet types; if (dynamic_cast(model)) { - types.insert(Colour3DPlot); + types.insert(Colour3DPlot); types.insert(Slice); } if (dynamic_cast(model)) { - types.insert(Waveform); + types.insert(Waveform); } if (dynamic_cast(model)) { - types.insert(Spectrogram); - types.insert(MelodicRangeSpectrogram); - types.insert(PeakFrequencySpectrogram); + types.insert(Spectrogram); + types.insert(MelodicRangeSpectrogram); + types.insert(PeakFrequencySpectrogram); } if (dynamic_cast(model)) { - types.insert(TimeInstants); + types.insert(TimeInstants); } if (dynamic_cast(model)) { - types.insert(TimeValues); + types.insert(TimeValues); } if (dynamic_cast(model)) { - types.insert(Notes); + types.insert(Notes); } // NOTE: GF: types is a set, so order of insertion does not matter if (dynamic_cast(model)) { - types.insert(FlexiNotes); + types.insert(FlexiNotes); } if (dynamic_cast(model)) { - types.insert(Regions); + types.insert(Regions); } if (dynamic_cast(model)) { - types.insert(Text); + types.insert(Text); } if (dynamic_cast(model)) { - types.insert(Image); + types.insert(Image); } if (dynamic_cast(model)) { @@ -304,47 +304,47 @@ LayerFactory::setModel(Layer *layer, Model *model) { // if (trySetModel(layer, model)) -// return; - +// return; + if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; // GF: added FlexiNoteLayer if (trySetModel(layer, model)) - return; - + return; + if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) - return; + return; if (trySetModel(layer, model)) return; @@ -357,21 +357,21 @@ LayerFactory::createEmptyModel(LayerType layerType, Model *baseModel) { if (layerType == TimeInstants) { - return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1); + return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1); } else if (layerType == TimeValues) { - return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true); + return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true); } else if (layerType == FlexiNotes) { - return new FlexiNoteModel(baseModel->getSampleRate(), 1, true); + return new FlexiNoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Notes) { - return new NoteModel(baseModel->getSampleRate(), 1, true); + return new NoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Regions) { - return new RegionModel(baseModel->getSampleRate(), 1, true); + return new RegionModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Text) { - return new TextModel(baseModel->getSampleRate(), 1, true); + return new TextModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Image) { - return new ImageModel(baseModel->getSampleRate(), 1, true); + return new ImageModel(baseModel->getSampleRate(), 1, true); } else { - return 0; + return 0; } } @@ -379,10 +379,10 @@ LayerFactory::getChannel(Layer *layer) { if (dynamic_cast(layer)) { - return dynamic_cast(layer)->getChannel(); + return dynamic_cast(layer)->getChannel(); } if (dynamic_cast(layer)) { - return dynamic_cast(layer)->getChannel(); + return dynamic_cast(layer)->getChannel(); } return -1; } @@ -391,16 +391,16 @@ LayerFactory::setChannel(Layer *layer, int channel) { if (dynamic_cast(layer)) { - dynamic_cast(layer)->setChannel(channel); - return; + dynamic_cast(layer)->setChannel(channel); + return; } if (dynamic_cast(layer)) { - dynamic_cast(layer)->setChannel(channel); - return; + dynamic_cast(layer)->setChannel(channel); + return; } if (dynamic_cast(layer)) { - dynamic_cast(layer)->setChannel(channel); - return; + dynamic_cast(layer)->setChannel(channel); + return; } } @@ -412,48 +412,48 @@ switch (type) { case Waveform: - layer = new WaveformLayer; - break; + layer = new WaveformLayer; + break; case Spectrogram: - layer = new SpectrogramLayer; - break; + layer = new SpectrogramLayer; + break; case TimeRuler: - layer = new TimeRulerLayer; - break; + layer = new TimeRulerLayer; + break; case TimeInstants: - layer = new TimeInstantLayer; - break; + layer = new TimeInstantLayer; + break; case TimeValues: - layer = new TimeValueLayer; - break; + layer = new TimeValueLayer; + break; case FlexiNotes: - layer = new FlexiNoteLayer; - break; + layer = new FlexiNoteLayer; + break; case Notes: - layer = new NoteLayer; - break; + layer = new NoteLayer; + break; case Regions: - layer = new RegionLayer; - break; + layer = new RegionLayer; + break; case Text: - layer = new TextLayer; - break; + layer = new TextLayer; + break; case Image: - layer = new ImageLayer; - break; + layer = new ImageLayer; + break; case Colour3DPlot: - layer = new Colour3DPlotLayer; - break; + layer = new Colour3DPlotLayer; + break; case Spectrum: layer = new SpectrumLayer; @@ -464,12 +464,12 @@ break; case MelodicRangeSpectrogram: - layer = new SpectrogramLayer(SpectrogramLayer::MelodicRange); - break; + layer = new SpectrogramLayer(SpectrogramLayer::MelodicRange); + break; case PeakFrequencySpectrogram: - layer = new SpectrogramLayer(SpectrogramLayer::MelodicPeaks); - break; + layer = new SpectrogramLayer(SpectrogramLayer::MelodicPeaks); + break; case UnknownLayer: default: @@ -478,12 +478,12 @@ } if (!layer) { - cerr << "LayerFactory::createLayer: Unknown layer type " - << type << endl; + cerr << "LayerFactory::createLayer: Unknown layer type " + << type << endl; } else { -// SVDEBUG << "LayerFactory::createLayer: Setting object name " -// << getLayerPresentationName(type) << " on " << layer << endl; - layer->setObjectName(getLayerPresentationName(type)); +// SVDEBUG << "LayerFactory::createLayer: Setting object name " +// << getLayerPresentationName(type) << " on " << layer << endl; + layer->setObjectName(getLayerPresentationName(type)); setLayerDefaultProperties(type, layer); } diff -r 57d192e26331 -r 13d9b422f7fe layer/LayerFactory.h --- a/layer/LayerFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LayerFactory.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _LAYER_FACTORY_H_ -#define _LAYER_FACTORY_H_ +#ifndef SV_LAYER_FACTORY_H +#define SV_LAYER_FACTORY_H #include #include @@ -28,27 +28,27 @@ public: enum LayerType { - // Standard layers - Waveform, - Spectrogram, - TimeRuler, - TimeInstants, - TimeValues, - Notes, - FlexiNotes, - Regions, - Text, + // Standard layers + Waveform, + Spectrogram, + TimeRuler, + TimeInstants, + TimeValues, + Notes, + FlexiNotes, + Regions, + Text, Image, - Colour3DPlot, + Colour3DPlot, Spectrum, Slice, - // Layers with different initial parameters - MelodicRangeSpectrogram, - PeakFrequencySpectrogram, + // Layers with different initial parameters + MelodicRangeSpectrogram, + PeakFrequencySpectrogram, - // Not-a-layer-type - UnknownLayer = 255 + // Not-a-layer-type + UnknownLayer = 255 }; static LayerFactory *getInstance(); @@ -89,12 +89,12 @@ protected: template bool trySetModel(Layer *layerBase, Model *modelBase) { - LayerClass *layer = dynamic_cast(layerBase); - if (!layer) return false; - ModelClass *model = dynamic_cast(modelBase); - if (!model) return false; - layer->setModel(model); - return true; + LayerClass *layer = dynamic_cast(layerBase); + if (!layer) return false; + ModelClass *model = dynamic_cast(modelBase); + if (!model) return false; + layer->setModel(model); + return true; } static LayerFactory *m_instance; diff -r 57d192e26331 -r 13d9b422f7fe layer/LinearColourScale.cpp --- a/layer/LinearColourScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LinearColourScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -24,18 +24,18 @@ int LinearColourScale::getWidth(LayerGeometryProvider *, - QPainter &paint) + QPainter &paint) { return paint.fontMetrics().width("-000.00") + 15; } void LinearColourScale::paintVertical(LayerGeometryProvider *v, - const ColourScaleLayer *layer, - QPainter &paint, - int /* x0 */, - double min, - double max) + const ColourScaleLayer *layer, + QPainter &paint, + int /* x0 */, + double min, + double max) { int h = v->getPaintHeight(); @@ -58,9 +58,9 @@ paint.save(); for (int y = 0; y < boxh; ++y) { - double val = ((boxh - y) * (max - min)) / boxh + min; - paint.setPen(layer->getColourForValue(v, val)); - paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); + double val = ((boxh - y) * (max - min)) / boxh + min; + paint.setPen(layer->getColourForValue(v, val)); + paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); } paint.restore(); @@ -78,19 +78,19 @@ for (int i = 0; i < n; ++i) { - int y, ty; + int y, ty; - y = boxy + int(boxh - ((val - min) * boxh) / (max - min)); + y = boxy + int(boxh - ((val - min) * boxh) / (max - min)); - ty = y - paint.fontMetrics().height() + - paint.fontMetrics().ascent() + 2; + ty = y - paint.fontMetrics().height() + + paint.fontMetrics().ascent() + 2; - snprintf(buffer, buflen, "%.*f", dp, val); - QString label = QString(buffer); + snprintf(buffer, buflen, "%.*f", dp, val); + QString label = QString(buffer); - paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y); - paint.drawText(tx, ty, label); + paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y); + paint.drawText(tx, ty, label); - val += inc; + val += inc; } } diff -r 57d192e26331 -r 13d9b422f7fe layer/LinearNumericalScale.cpp --- a/layer/LinearNumericalScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LinearNumericalScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2013 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,90 +15,65 @@ #include "LinearNumericalScale.h" #include "VerticalScaleLayer.h" +#include "LayerGeometryProvider.h" #include #include -#include "LayerGeometryProvider.h" +#include "base/ScaleTickIntervals.h" int LinearNumericalScale::getWidth(LayerGeometryProvider *, - QPainter &paint) + QPainter &paint) { return paint.fontMetrics().width("-000.00") + 10; } void LinearNumericalScale::paintVertical(LayerGeometryProvider *v, - const VerticalScaleLayer *layer, - QPainter &paint, - int x0, - double minf, - double maxf) + const VerticalScaleLayer *layer, + QPainter &paint, + int x0, + double minf, + double maxf) { int n = 10; - - double val = minf; - double inc = (maxf - val) / n; - - const int buflen = 40; - char buffer[buflen]; + auto ticks = ScaleTickIntervals::linear({ minf, maxf, n }); + n = int(ticks.size()); int w = getWidth(v, paint) + x0; - double round = 1.0; - int dp = 0; - if (inc > 0) { - int prec = int(trunc(log10(inc))); - prec -= 1; - if (prec < 0) dp = -prec; - round = pow(10.0, prec); -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "inc = " << inc << ", round = " << round << ", dp = " << dp << endl; -#endif - } + int prevy = -1; - int prevy = -1; - for (int i = 0; i < n; ++i) { - int y, ty; + int y, ty; bool drawText = true; - double dispval = val; - - if (i == n-1 && - v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { - if (layer->getScaleUnits() != "") drawText = false; - } - dispval = int(rint(val / round) * round); - -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "val = " << val << ", dispval = " << dispval << endl; -#endif - - y = layer->getYForValue(v, dispval); - - ty = y - paint.fontMetrics().height() + paint.fontMetrics().ascent() + 2; - - if (prevy >= 0 && (prevy - y) < paint.fontMetrics().height()) { - val += inc; - continue; + if (i == n-1 && + v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { + if (layer->getScaleUnits() != "") drawText = false; } - snprintf(buffer, buflen, "%.*f", dp, dispval); + double val = ticks[i].value; + QString label = QString::fromStdString(ticks[i].label); + + y = layer->getYForValue(v, val); - QString label = QString(buffer); + ty = y - paint.fontMetrics().height() + paint.fontMetrics().ascent() + 2; + + if (prevy >= 0 && (prevy - y) < paint.fontMetrics().height()) { + continue; + } - paint.drawLine(w - 5, y, w, y); + paint.drawLine(w - 5, y, w, y); if (drawText) { - paint.drawText(w - paint.fontMetrics().width(label) - 6, - ty, label); + paint.drawText(w - paint.fontMetrics().width(label) - 6, + ty, label); } prevy = y; - val += inc; } } diff -r 57d192e26331 -r 13d9b422f7fe layer/LinearNumericalScale.h --- a/layer/LinearNumericalScale.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LinearNumericalScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef LINEAR_NUMERICAL_SCALE_H -#define LINEAR_NUMERICAL_SCALE_H +#ifndef SV_LINEAR_NUMERICAL_SCALE_H +#define SV_LINEAR_NUMERICAL_SCALE_H #include diff -r 57d192e26331 -r 13d9b422f7fe layer/LogColourScale.cpp --- a/layer/LogColourScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LogColourScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -26,18 +26,18 @@ int LogColourScale::getWidth(LayerGeometryProvider *, - QPainter &paint) + QPainter &paint) { return paint.fontMetrics().width("-000.00") + 15; } void LogColourScale::paintVertical(LayerGeometryProvider *v, - const ColourScaleLayer *layer, - QPainter &paint, - int /* x0 */, - double minlog, - double maxlog) + const ColourScaleLayer *layer, + QPainter &paint, + int /* x0 */, + double minlog, + double maxlog) { int h = v->getPaintHeight(); @@ -60,9 +60,9 @@ paint.save(); for (int y = 0; y < boxh; ++y) { - double val = ((boxh - y) * (maxlog - minlog)) / boxh + minlog; - paint.setPen(layer->getColourForValue(v, LogRange::unmap(val))); - paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); + double val = ((boxh - y) * (maxlog - minlog)) / boxh + minlog; + paint.setPen(layer->getColourForValue(v, LogRange::unmap(val))); + paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); } paint.restore(); @@ -75,24 +75,24 @@ for (int i = 0; i < n; ++i) { - int y, ty; + int y, ty; - y = boxy + int(boxh - ((val - minlog) * boxh) / (maxlog - minlog)); + y = boxy + int(boxh - ((val - minlog) * boxh) / (maxlog - minlog)); - ty = y - paint.fontMetrics().height() + - paint.fontMetrics().ascent() + 2; + ty = y - paint.fontMetrics().height() + + paint.fontMetrics().ascent() + 2; - double dv = LogRange::unmap(val); - int digits = int(trunc(log10(dv))); - int sf = dp + (digits > 0 ? digits : 0); - if (sf < 2) sf = 2; - snprintf(buffer, buflen, "%.*g", sf, dv); + double dv = LogRange::unmap(val); + int digits = int(trunc(log10(dv))); + int sf = dp + (digits > 0 ? digits : 0); + if (sf < 2) sf = 2; + snprintf(buffer, buflen, "%.*g", sf, dv); - QString label = QString(buffer); + QString label = QString(buffer); - paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y); - paint.drawText(tx, ty, label); + paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y); + paint.drawText(tx, ty, label); - val += inc; + val += inc; } } diff -r 57d192e26331 -r 13d9b422f7fe layer/LogNumericalScale.cpp --- a/layer/LogNumericalScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LogNumericalScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2013 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,108 +15,67 @@ #include "LogNumericalScale.h" #include "VerticalScaleLayer.h" +#include "LayerGeometryProvider.h" #include "base/LogRange.h" +#include "base/ScaleTickIntervals.h" #include #include -#include "LayerGeometryProvider.h" - -//#define DEBUG_TIME_VALUE_LAYER 1 - int LogNumericalScale::getWidth(LayerGeometryProvider *, - QPainter &paint) + QPainter &paint) { return paint.fontMetrics().width("-000.00") + 10; } void LogNumericalScale::paintVertical(LayerGeometryProvider *v, - const VerticalScaleLayer *layer, - QPainter &paint, - int x0, - double minlog, - double maxlog) + const VerticalScaleLayer *layer, + QPainter &paint, + int x0, + double minlog, + double maxlog) { + int n = 10; + auto ticks = ScaleTickIntervals::logarithmicAlready({ minlog, maxlog, n }); + n = int(ticks.size()); + int w = getWidth(v, paint) + x0; - int n = 10; - - double val = minlog; - double inc = (maxlog - val) / n; // even increments of log scale - - // smallest increment as displayed - double minDispInc = LogRange::unmap(minlog + inc) - LogRange::unmap(minlog); - -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "min = " << minlog << ", max = " << maxlog << ", inc = " << inc << ", minDispInc = " << minDispInc << endl; -#endif - - const int buflen = 40; - char buffer[buflen]; - - double round = 1.f; - int dp = 0; - - if (minDispInc > 0) { - int prec = int(trunc(log10(minDispInc))); - if (prec < 0) dp = -prec; - round = pow(10.0, prec); - if (dp > 4) dp = 4; -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "round = " << round << ", prec = " << prec << ", dp = " << dp << endl; -#endif - } - int prevy = -1; for (int i = 0; i < n; ++i) { - int y, ty; + int y, ty; bool drawText = true; - if (i == n-1 && - v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { - if (layer->getScaleUnits() != "") drawText = false; - } - - double dispval = LogRange::unmap(val); - dispval = floor(dispval / round) * round; - -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "val = " << val << ", dispval = " << dispval << endl; -#endif - - y = layer->getYForValue(v, dispval); - - ty = y - paint.fontMetrics().height() + paint.fontMetrics().ascent() + 2; - - if (prevy >= 0 && (prevy - y) < paint.fontMetrics().height()) { - val += inc; - continue; + if (i == n-1 && + v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { + if (layer->getScaleUnits() != "") drawText = false; } - int digits = int(trunc(log10(dispval))); - int sf = dp + (digits > 0 ? digits : 0); - if (sf < 4) sf = 4; -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "sf = " << sf << endl; -#endif - snprintf(buffer, buflen, "%.*g", sf, dispval); + double val = ticks[i].value; + QString label = QString::fromStdString(ticks[i].label); - QString label = QString(buffer); + y = layer->getYForValue(v, val); - paint.drawLine(w - 5, y, w, y); + ty = y - paint.fontMetrics().height() + paint.fontMetrics().ascent() + 2; + + if (prevy >= 0 && (prevy - y) < paint.fontMetrics().height()) { + continue; + } + + paint.drawLine(w - 5, y, w, y); if (drawText) { - paint.drawText(w - paint.fontMetrics().width(label) - 6, - ty, label); + paint.drawText(w - paint.fontMetrics().width(label) - 6, + ty, label); } prevy = y; - val += inc; } } + diff -r 57d192e26331 -r 13d9b422f7fe layer/LogNumericalScale.h --- a/layer/LogNumericalScale.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/LogNumericalScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2013 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef LOG_NUMERICAL_SCALE_H -#define LOG_NUMERICAL_SCALE_H +#ifndef SV_LOG_NUMERICAL_SCALE_H +#define SV_LOG_NUMERICAL_SCALE_H #include @@ -28,8 +28,8 @@ int getWidth(LayerGeometryProvider *v, QPainter &paint); void paintVertical - (LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, - double minlog, double maxlog); + (LayerGeometryProvider *v, const VerticalScaleLayer *layer, + QPainter &paint, int x0, double minlog, double maxlog); }; #endif diff -r 57d192e26331 -r 13d9b422f7fe layer/NoteLayer.cpp --- a/layer/NoteLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/NoteLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -61,12 +61,12 @@ m_scaleMinimum(0), m_scaleMaximum(0) { - SVDEBUG << "constructed NoteLayer" << endl; + SVDEBUG << "constructed NoteLayer" << endl; } void NoteLayer::setModel(NoteModel *model) -{ +{ if (m_model == model) return; m_model = model; @@ -128,12 +128,12 @@ int val = 0; if (name == "Vertical Scale") { - - if (min) *min = 0; - if (max) *max = 3; + + if (min) *min = 0; + if (max) *max = 3; if (deflt) *deflt = int(AutoAlignScale); - - val = int(m_verticalScale); + + val = int(m_verticalScale); } else if (name == "Scale Units") { @@ -145,7 +145,7 @@ } else { - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -156,13 +156,13 @@ int value) const { if (name == "Vertical Scale") { - switch (value) { - default: - case 0: return tr("Auto-Align"); - case 1: return tr("Linear"); - case 2: return tr("Log"); - case 3: return tr("MIDI Notes"); - } + switch (value) { + default: + case 0: return tr("Auto-Align"); + case 1: return tr("Linear"); + case 2: return tr("Log"); + case 3: return tr("MIDI Notes"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -171,7 +171,7 @@ NoteLayer::setProperty(const PropertyName &name, int value) { if (name == "Vertical Scale") { - setVerticalScale(VerticalScale(value)); + setVerticalScale(VerticalScale(value)); } else if (name == "Scale Units") { if (m_model) { m_model->setScaleUnits @@ -397,36 +397,36 @@ sv_frame_t frame = v->getFrameForX(x); NoteModel::PointList onPoints = - m_model->getPoints(frame); + m_model->getPoints(frame); if (!onPoints.empty()) { - return onPoints; + return onPoints; } NoteModel::PointList prevPoints = - m_model->getPreviousPoints(frame); + m_model->getPreviousPoints(frame); NoteModel::PointList nextPoints = - m_model->getNextPoints(frame); + m_model->getNextPoints(frame); NoteModel::PointList usePoints = prevPoints; if (prevPoints.empty()) { - usePoints = nextPoints; + usePoints = nextPoints; } else if (int(prevPoints.begin()->frame) < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; + !(nextPoints.begin()->frame > v->getEndFrame())) { + usePoints = nextPoints; } else if (int(nextPoints.begin()->frame) - frame < - frame - int(prevPoints.begin()->frame)) { - usePoints = nextPoints; + frame - int(prevPoints.begin()->frame)) { + usePoints = nextPoints; } if (!usePoints.empty()) { - int fuzz = 2; - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } + int fuzz = 2; + int px = v->getXForFrame(usePoints.begin()->frame); + if ((px > x && px - x > fuzz) || + (px < x && x - px > fuzz + 1)) { + usePoints.clear(); + } } return usePoints; @@ -470,11 +470,11 @@ NoteModel::PointList points = getLocalPoints(v, x); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return tr("No local points"); - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return tr("No local points"); + } } Note note(0); @@ -482,26 +482,26 @@ for (i = points.begin(); i != points.end(); ++i) { - int y = getYForValue(v, i->value); - int h = 3; + int y = getYForValue(v, i->value); + int h = 3; - if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, i->value + m_model->getValueQuantization()); - if (h < 3) h = 3; - } + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + if (h < 3) h = 3; + } - if (pos.y() >= y - h && pos.y() <= y) { - note = *i; - break; - } + if (pos.y() >= y - h && pos.y() <= y) { + note = *i; + break; + } } if (i == points.end()) return tr("No local points"); RealTime rt = RealTime::frame2RealTime(note.frame, - m_model->getSampleRate()); + m_model->getSampleRate()); RealTime rd = RealTime::frame2RealTime(note.duration, - m_model->getSampleRate()); + m_model->getSampleRate()); QString pitchText; @@ -530,41 +530,41 @@ QString text; if (note.label == "") { - text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) - .arg(rt.toText(true).c_str()) - .arg(pitchText) - .arg(rd.toText(true).c_str()); + text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) + .arg(rt.toText(true).c_str()) + .arg(pitchText) + .arg(rd.toText(true).c_str()); } else { - text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) - .arg(rt.toText(true).c_str()) - .arg(pitchText) - .arg(rd.toText(true).c_str()) - .arg(note.label); + text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) + .arg(rt.toText(true).c_str()) + .arg(pitchText) + .arg(rd.toText(true).c_str()) + .arg(note.label); } pos = QPoint(v->getXForFrame(note.frame), - getYForValue(v, note.value)); + getYForValue(v, note.value)); return text; } bool NoteLayer::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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); NoteModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame)); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -572,47 +572,47 @@ bool found = false; for (NoteModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } + if (i->frame > frame) { + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - NoteModel::PointList::const_iterator j = i; - ++j; + NoteModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -764,7 +764,7 @@ brushColour.setAlpha(80); // SVDEBUG << "NoteLayer::paint: resolution is " -// << m_model->getResolution() << " frames" << endl; +// << m_model->getResolution() << " frames" << endl; double min = m_model->getValueMinimum(); double max = m_model->getValueMaximum(); @@ -783,25 +783,25 @@ paint.setRenderHint(QPainter::Antialiasing, false); for (NoteModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const NoteModel::Point &p(*i); + const NoteModel::Point &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, p.value); - int w = v->getXForFrame(p.frame + p.duration) - x; - int h = 3; - - if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, p.value + m_model->getValueQuantization()); - if (h < 3) h = 3; - } + int x = v->getXForFrame(p.frame); + int y = getYForValue(v, p.value); + int w = v->getXForFrame(p.frame + p.duration) - x; + int h = 3; + + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + if (h < 3) h = 3; + } - if (w < 1) w = 1; - paint.setPen(getBaseQColor()); - paint.setBrush(brushColour); + if (w < 1) w = 1; + paint.setPen(getBaseQColor()); + paint.setBrush(brushColour); - if (shouldIlluminate && + if (shouldIlluminate && // "illuminatePoint == p" !NoteModel::Point::Comparator()(illuminatePoint, p) && !NoteModel::Point::Comparator()(p, illuminatePoint)) { @@ -822,9 +822,9 @@ x, y - h/2 - paint.fontMetrics().descent() - 2, hlabel, PaintAssistant::OutlinedText); - } - - paint.drawRect(x, y - h/2, w, h); + } + + paint.drawRect(x, y - h/2, w, h); } paint.restore(); @@ -833,14 +833,18 @@ int NoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { - if (!m_model || shouldAutoAlign()) { + if (!m_model) { return 0; - } else { - if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { - return LogNumericalScale().getWidth(v, paint) + 10; // for piano - } else { - return LinearNumericalScale().getWidth(v, paint); - } + } + + if (shouldAutoAlign() && !valueExtentsMatchMine(v)) { + return 0; + } + + if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { + return LogNumericalScale().getWidth(v, paint) + 10; // for piano + } else { + return LinearNumericalScale().getWidth(v, paint); } } @@ -900,7 +904,7 @@ if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new NoteModel::EditCommand(m_model, - tr("Draw Point")); + tr("Draw Point")); m_editingCommand->addPoint(m_editingPoint); m_editing = true; @@ -953,8 +957,8 @@ if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -999,8 +1003,8 @@ m_dragPointY = getYForValue(v, m_editingPoint.value); if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -1027,8 +1031,8 @@ double value = getValueForY(v, newy); if (!m_editingCommand) { - m_editingCommand = new NoteModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new NoteModel::EditCommand(m_model, + tr("Drag Point")); } m_editingCommand->deletePoint(m_editingPoint); @@ -1045,20 +1049,20 @@ if (m_editingCommand) { - QString newName = m_editingCommand->getName(); + QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { - newName = tr("Edit Point"); - } else { - newName = tr("Relocate Point"); - } - } else { - newName = tr("Change Point Value"); - } + if (m_editingPoint.frame != m_originalPoint.frame) { + if (m_editingPoint.value != m_originalPoint.value) { + newName = tr("Edit Point"); + } else { + newName = tr("Relocate Point"); + } + } else { + newName = tr("Change Point Value"); + } - m_editingCommand->setName(newName); - finish(m_editingCommand); + m_editingCommand->setName(newName); + finish(m_editingCommand); } m_editingCommand = 0; @@ -1113,20 +1117,20 @@ if (!m_model) return; NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Drag Selection")); + new NoteModel::EditCommand(m_model, tr("Drag Selection")); NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + 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); - } + if (s.contains(i->frame)) { + NoteModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1138,34 +1142,34 @@ if (!m_model) return; NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Resize Selection")); + new NoteModel::EditCommand(m_model, tr("Resize Selection")); NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double targetStart = double(i->frame); - targetStart = double(newSize.getStartFrame()) + - targetStart - double(s.getStartFrame()) * ratio; + 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; + 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); - } + NoteModel::Point newPoint(*i); + newPoint.frame = lrint(targetStart); + newPoint.duration = lrint(targetEnd - targetStart); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1177,13 +1181,13 @@ if (!m_model) return; NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Delete Selected Points")); + new NoteModel::EditCommand(m_model, tr("Delete Selected Points")); NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { if (s.contains(i->frame)) { command->deletePoint(*i); @@ -1199,11 +1203,11 @@ if (!m_model) return; NoteModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (NoteModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -1238,7 +1242,7 @@ } NoteModel::EditCommand *command = - new NoteModel::EditCommand(m_model, tr("Paste")); + new NoteModel::EditCommand(m_model, tr("Paste")); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -1350,7 +1354,7 @@ bool ok, alsoOk; VerticalScale scale = (VerticalScale) - attributes.value("verticalScale").toInt(&ok); + attributes.value("verticalScale").toInt(&ok); if (ok) setVerticalScale(scale); float min = attributes.value("scaleMinimum").toFloat(&ok); diff -r 57d192e26331 -r 13d9b422f7fe layer/NoteLayer.h --- a/layer/NoteLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/NoteLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -43,8 +43,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); @@ -78,7 +78,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); enum VerticalScale { diff -r 57d192e26331 -r 13d9b422f7fe layer/PaintAssistant.cpp --- a/layer/PaintAssistant.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/PaintAssistant.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -19,6 +19,7 @@ #include "base/AudioLevel.h" #include "base/Strings.h" +#include "base/Debug.h" #include #include @@ -28,7 +29,7 @@ void PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect, - double minVal, double maxVal, + double minVal, double maxVal, Scale scale, int &mult, std::vector *vy) { @@ -55,13 +56,11 @@ } while (!round && mult < limit); if (round) { mult /= 10; -// cerr << "\n\nstep goes from " << step; step = double(round) / mult; n = int(lrint((maxVal - minVal) / step)); if (mult > 1) { mult /= 10; } -// cerr << " to " << step << " (n = " << n << ")" << endl; } } @@ -116,7 +115,6 @@ if (spaceForLabel) { int tx = 3; -// if (scale != LinearScale) { if (paint.fontMetrics().width(text) < w - 10) { tx = w - 10 - paint.fontMetrics().width(text); } @@ -125,8 +123,6 @@ if (ty < paint.fontMetrics().ascent()) { ty = paint.fontMetrics().ascent(); -// } else if (ty > rect.y() + h - paint.fontMetrics().descent()) { -// ty = rect.y() + h - paint.fontMetrics().descent(); } else { ty += toff; } @@ -134,19 +130,7 @@ paint.drawText(tx, ty, text); lastLabelledY = ty - toff; - /* - if (ny != y) { - ty = ny; - if (ty < paint.fontMetrics().ascent()) { - ty = paint.fontMetrics().ascent(); - } else if (ty > h - paint.fontMetrics().descent()) { - ty = h - paint.fontMetrics().descent(); - } else { - ty += toff; - } - paint.drawText(tx, ty, text); - } - */ + paint.drawLine(w - 7, y, w, y); if (vy) vy->push_back(y); @@ -187,18 +171,13 @@ { int vy = 0; -// int m = height/2; -// int my = minY + m; - switch (scale) { case LinearScale: -// vy = my - int(m * value); vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height); break; case MeterScale: -// vy = my - AudioLevel::multiplier_to_preview(value, m); vy = minY + height - AudioLevel::multiplier_to_preview ((value - minVal) / (maxVal - minVal), height); break; @@ -238,22 +217,21 @@ QRect r = paint.fontMetrics().boundingRect(text); r.translate(QPoint(x, y)); -// cerr << "drawVisibleText: r = " << r.x() << "," < +#include #include class QPainter; @@ -38,14 +39,26 @@ int minY, int height); enum TextStyle { - BoxedText, - OutlinedText, + BoxedText, + OutlinedText, OutlinedItalicText }; static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style); + + /** + * Scale up pen width for a hi-dpi display without pixel doubling. + * Very similar to ViewManager::scalePixelSize, but a bit more + * conservative. + */ + static double scalePenWidth(double width); + + /** + * Apply scalePenWidth to a pen. + */ + static QPen scalePen(QPen pen); }; #endif diff -r 57d192e26331 -r 13d9b422f7fe layer/PianoScale.cpp --- a/layer/PianoScale.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/PianoScale.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -22,13 +22,17 @@ #include "base/Pitch.h" #include "LayerGeometryProvider.h" +#include "HorizontalScaleProvider.h" + +#include +using namespace std; void PianoScale::paintPianoVertical(LayerGeometryProvider *v, - QPainter &paint, - QRect r, - double minf, - double maxf) + QPainter &paint, + QRect r, + double minf, + double maxf) { int x0 = r.x(), y0 = r.y(), x1 = r.x() + r.width(), y1 = r.y() + r.height(); @@ -39,47 +43,119 @@ for (int i = 0; i < 128; ++i) { - double f = Pitch::getFrequencyForPitch(i); - int y = int(lrint(v->getYForFrequency(f, minf, maxf, true))); + double f = Pitch::getFrequencyForPitch(i); + int y = int(lrint(v->getYForFrequency(f, minf, maxf, true))); - if (y < y0 - 2) break; - if (y > y1 + 2) { - continue; - } - - int n = (i % 12); - - if (n == 1) { - // C# -- fill the C from here - QColor col = Qt::gray; - if (i == 61) { // filling middle C - col = Qt::blue; - col = col.light(150); - } - if (ppy - y > 2) { - paint.fillRect(x0 + 1, - y, - x1 - x0, - (py + ppy) / 2 - y, - col); - } - } - - if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { - // black notes - paint.drawLine(x0 + 1, y, x1, y); - int rh = ((py - y) / 4) * 2; - if (rh < 2) rh = 2; - paint.drawRect(x0 + 1, y - (py-y)/4, (x1 - x0) / 2, rh); - } else if (n == 0 || n == 5) { - // C, F - if (py < y1) { - paint.drawLine(x0 + 1, (y + py) / 2, x1, (y + py) / 2); - } - } - - ppy = py; - py = y; + if (y < y0 - 2) break; + if (y > y1 + 2) { + continue; + } + + int n = (i % 12); + + if (n == 1) { + // C# -- fill the C from here + QColor col = Qt::gray; + if (i == 61) { // filling middle C + col = Qt::blue; + col = col.light(150); + } + if (ppy - y > 2) { + paint.fillRect(x0 + 1, + y, + x1 - x0, + (py + ppy) / 2 - y, + col); + } + } + + if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { + // black notes + paint.drawLine(x0 + 1, y, x1, y); + int rh = ((py - y) / 4) * 2; + if (rh < 2) rh = 2; + paint.drawRect(x0 + 1, y - (py-y)/4, (x1 - x0) / 2, rh); + } else if (n == 0 || n == 5) { + // C, F + if (py < y1) { + paint.drawLine(x0 + 1, (y + py) / 2, x1, (y + py) / 2); + } + } + + ppy = py; + py = y; } } +void +PianoScale::paintPianoHorizontal(LayerGeometryProvider *v, + const HorizontalScaleProvider *p, + QPainter &paint, + QRect r) +{ + int x0 = r.x(), y0 = r.y(), x1 = r.x() + r.width(), y1 = r.y() + r.height(); + + paint.drawLine(x0, y0, x1, y0); + + int px = x0, ppx = x0; + paint.setBrush(paint.pen().color()); + + for (int i = 0; i < 128; ++i) { + + double f = Pitch::getFrequencyForPitch(i); + int x = int(lrint(p->getXForFrequency(v, f))); + + if (i == 0) { + px = ppx = x; + } + if (i == 1) { + ppx = px - (x - px); + } + + if (x < x0) { + ppx = px; + px = x; + continue; + } + + if (x > x1) { + break; + } + + int n = (i % 12); + + if (n == 1) { + // C# -- fill the C from here + QColor col = Qt::gray; + if (i == 61) { // filling middle C + col = Qt::blue; + col = col.light(150); + } + if (x - ppx > 2) { + paint.fillRect((px + ppx) / 2 + 1, + y0 + 1, + x - (px + ppx) / 2 - 1, + y1 - y0, + col); + } + } + + if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { + // black notes + paint.drawLine(x, y0, x, y1); + int rw = int(lrint(double(x - px) / 4) * 2); + if (rw < 2) rw = 2; + paint.drawRect(x - rw/2, (y0 + y1) / 2, rw, (y1 - y0) / 2); + } else if (n == 0 || n == 5) { + // C, F + if (px < x1) { + paint.drawLine((x + px) / 2, y0, (x + px) / 2, y1); + } + } + + ppx = px; + px = x; + } +} + + diff -r 57d192e26331 -r 13d9b422f7fe layer/PianoScale.h --- a/layer/PianoScale.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/PianoScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -20,12 +20,18 @@ class QPainter; class LayerGeometryProvider; +class HorizontalScaleProvider; class PianoScale { public: void paintPianoVertical - (LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf); + (LayerGeometryProvider *v, QPainter &paint, QRect rect, + double minf, double maxf); + + void paintPianoHorizontal + (LayerGeometryProvider *v, const HorizontalScaleProvider *p, + QPainter &paint, QRect rect); }; #endif diff -r 57d192e26331 -r 13d9b422f7fe layer/RegionLayer.cpp --- a/layer/RegionLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/RegionLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -138,20 +138,20 @@ val = m_colourMap; } else if (name == "Plot Type") { - - if (min) *min = 0; - if (max) *max = 1; + + if (min) *min = 0; + if (max) *max = 1; if (deflt) *deflt = 0; - - val = int(m_plotStyle); + + val = int(m_plotStyle); } else if (name == "Vertical Scale") { - - if (min) *min = 0; - if (max) *max = 3; + + if (min) *min = 0; + if (max) *max = 3; if (deflt) *deflt = int(EqualSpaced); - - val = int(m_verticalScale); + + val = int(m_verticalScale); } else if (name == "Scale Units") { @@ -163,7 +163,7 @@ } else { - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -177,20 +177,20 @@ return ColourMapper::getColourMapName(value); } else if (name == "Plot Type") { - switch (value) { - default: - case 0: return tr("Bars"); - case 1: return tr("Segmentation"); - } + switch (value) { + default: + case 0: return tr("Bars"); + case 1: return tr("Segmentation"); + } } else if (name == "Vertical Scale") { - switch (value) { - default: - case 0: return tr("Auto-Align"); - case 1: return tr("Equal Spaced"); - case 2: return tr("Linear"); - case 3: return tr("Log"); - } + switch (value) { + default: + case 0: return tr("Auto-Align"); + case 1: return tr("Equal Spaced"); + case 2: return tr("Linear"); + case 3: return tr("Log"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -201,9 +201,9 @@ if (name == "Colour" && m_plotStyle == PlotSegmentation) { setFillColourMap(value); } else if (name == "Plot Type") { - setPlotStyle(PlotStyle(value)); + setPlotStyle(PlotStyle(value)); } else if (name == "Vertical Scale") { - setVerticalScale(VerticalScale(value)); + setVerticalScale(VerticalScale(value)); } else if (name == "Scale Units") { if (m_model) { m_model->setScaleUnits @@ -310,36 +310,36 @@ sv_frame_t frame = v->getFrameForX(x); RegionModel::PointList onPoints = - m_model->getPoints(frame); + m_model->getPoints(frame); if (!onPoints.empty()) { - return onPoints; + return onPoints; } RegionModel::PointList prevPoints = - m_model->getPreviousPoints(frame); + m_model->getPreviousPoints(frame); RegionModel::PointList nextPoints = - m_model->getNextPoints(frame); + m_model->getNextPoints(frame); RegionModel::PointList usePoints = prevPoints; if (prevPoints.empty()) { - usePoints = nextPoints; + usePoints = nextPoints; } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; + !(nextPoints.begin()->frame > v->getEndFrame())) { + usePoints = nextPoints; } else if (long(nextPoints.begin()->frame) - frame < - frame - long(prevPoints.begin()->frame)) { - usePoints = nextPoints; + frame - long(prevPoints.begin()->frame)) { + usePoints = nextPoints; } if (!usePoints.empty()) { - int fuzz = 2; - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } + int fuzz = 2; + int px = v->getXForFrame(usePoints.begin()->frame); + if ((px > x && px - x > fuzz) || + (px < x && x - px > fuzz + 1)) { + usePoints.clear(); + } } return usePoints; @@ -393,11 +393,11 @@ RegionModel::PointList points = getLocalPoints(v, x); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return tr("No local points"); - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return tr("No local points"); + } } RegionRec region(0); @@ -408,26 +408,26 @@ for (i = points.begin(); i != points.end(); ++i) { - int y = getYForValue(v, i->value); - int h = 3; + int y = getYForValue(v, i->value); + int h = 3; - if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, i->value + m_model->getValueQuantization()); - if (h < 3) h = 3; - } + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + if (h < 3) h = 3; + } - if (pos.y() >= y - h && pos.y() <= y) { - region = *i; - break; - } + if (pos.y() >= y - h && pos.y() <= y) { + region = *i; + break; + } } if (i == points.end()) return tr("No local points"); RealTime rt = RealTime::frame2RealTime(region.frame, - m_model->getSampleRate()); + m_model->getSampleRate()); RealTime rd = RealTime::frame2RealTime(region.duration, - m_model->getSampleRate()); + m_model->getSampleRate()); QString valueText; @@ -436,20 +436,20 @@ QString text; if (region.label == "") { - text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) - .arg(rt.toText(true).c_str()) - .arg(valueText) - .arg(rd.toText(true).c_str()); + text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) + .arg(rt.toText(true).c_str()) + .arg(valueText) + .arg(rd.toText(true).c_str()); } else { - text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4")) - .arg(rt.toText(true).c_str()) - .arg(valueText) - .arg(rd.toText(true).c_str()) - .arg(region.label); + text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4")) + .arg(rt.toText(true).c_str()) + .arg(valueText) + .arg(rd.toText(true).c_str()) + .arg(region.label); } pos = QPoint(v->getXForFrame(region.frame), - getYForValue(v, region.value)); + getYForValue(v, region.value)); return text; } @@ -459,18 +459,18 @@ SnapType snap) const { if (!m_model) { - return Layer::snapToFeatureFrame(v, frame, resolution, snap); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); RegionModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame)); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -478,15 +478,15 @@ bool found = false; for (RegionModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { // 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 <= frame) { if (i->frame + i->duration > frame) { snapped = i->frame + i->duration; found = true; // don't break, as the next may be better @@ -499,37 +499,37 @@ break; } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - RegionModel::PointList::const_iterator j = i; - ++j; + RegionModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -542,7 +542,7 @@ SnapType snap) const { if (!m_model) { - return Layer::snapToSimilarFeature(v, frame, resolution, snap); + return Layer::snapToSimilarFeature(v, frame, resolution, snap); } resolution = m_model->getResolution(); @@ -586,29 +586,29 @@ } } - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > matchframe && + if (i->frame > matchframe && fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; - break; - } + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame < matchframe) { + 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 if (found || distant) { + break; + } - } else { + } else { // no other snap types supported - } + } ++i; } @@ -889,7 +889,7 @@ brushColour.setAlpha(80); // SVDEBUG << "RegionLayer::paint: resolution is " -// << m_model->getResolution() << " frames" << endl; +// << m_model->getResolution() << " frames" << endl; double min = m_model->getValueMinimum(); double max = m_model->getValueMaximum(); @@ -916,33 +916,33 @@ int fontHeight = paint.fontMetrics().height(); for (RegionModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const RegionModel::Point &p(*i); + const RegionModel::Point &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, p.value); - int w = v->getXForFrame(p.frame + p.duration) - x; - int h = 9; - int ex = x + w; + int x = v->getXForFrame(p.frame); + int y = getYForValue(v, p.value); + int w = v->getXForFrame(p.frame + p.duration) - x; + int h = 9; + int ex = x + w; RegionModel::PointList::const_iterator j = i; - ++j; + ++j; - if (j != points.end()) { - const RegionModel::Point &q(*j); - int nx = v->getXForFrame(q.frame); + if (j != points.end()) { + const RegionModel::Point &q(*j); + int nx = v->getXForFrame(q.frame); if (nx < ex) ex = nx; } - if (m_model->getValueQuantization() != 0.0) { - h = y - getYForValue(v, p.value + m_model->getValueQuantization()); - if (h < 3) h = 3; - } + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + if (h < 3) h = 3; + } - if (w < 1) w = 1; + if (w < 1) w = 1; - if (m_plotStyle == PlotSegmentation) { + if (m_plotStyle == PlotSegmentation) { paint.setPen(getForegroundQColor(v->getView())); paint.setBrush(getColourForValue(v, p.value)); } else { @@ -950,9 +950,9 @@ paint.setBrush(brushColour); } - if (m_plotStyle == PlotSegmentation) { + if (m_plotStyle == PlotSegmentation) { - if (ex <= x) continue; + if (ex <= x) continue; if (!shouldIlluminate || // "illuminatePoint != p" @@ -967,9 +967,9 @@ paint.setPen(QPen(getForegroundQColor(v->getView()), 2)); } - paint.drawRect(x, -1, ex - x, v->getPaintHeight() + 2); + paint.drawRect(x, -1, ex - x, v->getPaintHeight() + 2); - } else { + } else { if (shouldIlluminate && // "illuminatePoint == p" @@ -1005,16 +1005,16 @@ int lastLabelY = 0; for (RegionModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const RegionModel::Point &p(*i); + const RegionModel::Point &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, p.value); + int x = v->getXForFrame(p.frame); + int y = getYForValue(v, p.value); bool illuminated = false; - if (m_plotStyle != PlotSegmentation) { + if (m_plotStyle != PlotSegmentation) { if (shouldIlluminate && // "illuminatePoint == p" @@ -1194,8 +1194,8 @@ if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -1244,8 +1244,8 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -1278,8 +1278,8 @@ double value = getValueForY(v, newy, avoid); if (!m_editingCommand) { - m_editingCommand = new RegionModel::EditCommand(m_model, - tr("Drag Region")); + m_editingCommand = new RegionModel::EditCommand(m_model, + tr("Drag Region")); } m_editingCommand->deletePoint(m_editingPoint); @@ -1296,20 +1296,20 @@ if (m_editingCommand) { - QString newName = m_editingCommand->getName(); + QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { - newName = tr("Edit Region"); - } else { - newName = tr("Relocate Region"); - } - } else { - newName = tr("Change Point Value"); - } + if (m_editingPoint.frame != m_originalPoint.frame) { + if (m_editingPoint.value != m_originalPoint.value) { + newName = tr("Edit Region"); + } else { + newName = tr("Relocate Region"); + } + } else { + newName = tr("Change Point Value"); + } - m_editingCommand->setName(newName); - finish(m_editingCommand); + m_editingCommand->setName(newName); + finish(m_editingCommand); } m_editingCommand = 0; @@ -1364,20 +1364,20 @@ if (!m_model) return; RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Drag Selection")); + new RegionModel::EditCommand(m_model, tr("Drag Selection")); RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + 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); - } + if (s.contains(i->frame)) { + RegionModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1390,34 +1390,34 @@ if (!m_model) return; RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Resize Selection")); + new RegionModel::EditCommand(m_model, tr("Resize Selection")); RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double targetStart = double(i->frame); - targetStart = double(newSize.getStartFrame()) + - targetStart - double(s.getStartFrame()) * ratio; + 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; + 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); - } + RegionModel::Point newPoint(*i); + newPoint.frame = lrint(targetStart); + newPoint.duration = lrint(targetEnd - targetStart); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1430,13 +1430,13 @@ if (!m_model) return; RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); + new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { if (s.contains(i->frame)) { command->deletePoint(*i); @@ -1453,11 +1453,11 @@ if (!m_model) return; RegionModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (RegionModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -1492,7 +1492,7 @@ } RegionModel::EditCommand *command = - new RegionModel::EditCommand(m_model, tr("Paste")); + new RegionModel::EditCommand(m_model, tr("Paste")); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -1563,10 +1563,10 @@ bool ok; VerticalScale scale = (VerticalScale) - attributes.value("verticalScale").toInt(&ok); + attributes.value("verticalScale").toInt(&ok); if (ok) setVerticalScale(scale); PlotStyle style = (PlotStyle) - attributes.value("plotStyle").toInt(&ok); + attributes.value("plotStyle").toInt(&ok); if (ok) setPlotStyle(style); } diff -r 57d192e26331 -r 13d9b422f7fe layer/RegionLayer.h --- a/layer/RegionLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/RegionLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -48,8 +48,8 @@ virtual QString getLabelPreceding(sv_frame_t) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap) const; @@ -86,7 +86,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); void setFillColourMap(int); @@ -103,8 +103,8 @@ VerticalScale getVerticalScale() const { return m_verticalScale; } enum PlotStyle { - PlotLines, - PlotSegmentation + PlotLines, + PlotSegmentation }; void setPlotStyle(PlotStyle style); diff -r 57d192e26331 -r 13d9b422f7fe layer/RenderTimer.h --- a/layer/RenderTimer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/RenderTimer.h Mon Sep 17 13:51:31 2018 +0100 @@ -21,16 +21,16 @@ { public: enum Type { - /// A normal rendering operation with normal responsiveness demands - FastRender, + /// A normal rendering operation with normal responsiveness demands + FastRender, - /// An operation that the user might accept being slower - SlowRender, + /// An operation that the user might accept being slower + SlowRender, - /// An operation that should always complete, i.e. as if there - /// were no RenderTimer in use, but without having to change - /// client code structurally - NoTimeout + /// An operation that should always complete, i.e. as if there + /// were no RenderTimer in use, but without having to change + /// client code structurally + NoTimeout }; /** @@ -41,19 +41,19 @@ * happened. */ RenderTimer(Type t) : - m_start(std::chrono::steady_clock::now()), - m_haveLimits(true), - m_minFraction(0.1), - m_softLimit(0.1), - m_hardLimit(0.2), - m_softLimitOverridden(false) { + m_start(std::chrono::steady_clock::now()), + m_haveLimits(true), + m_minFraction(0.1), + m_softLimit(0.1), + m_hardLimit(0.2), + m_softLimitOverridden(false) { - if (t == NoTimeout) { - m_haveLimits = false; - } else if (t == SlowRender) { - m_softLimit = 0.2; - m_hardLimit = 0.4; - } + if (t == NoTimeout) { + m_haveLimits = false; + } else if (t == SlowRender) { + m_softLimit = 0.2; + m_hardLimit = 0.4; + } } @@ -66,36 +66,46 @@ */ bool outOfTime(double fractionComplete) { - if (!m_haveLimits || fractionComplete < m_minFraction) { - return false; - } - - auto t = std::chrono::steady_clock::now(); - double elapsed = std::chrono::duration(t - m_start).count(); - - if (elapsed > m_hardLimit) { - return true; - } else if (!m_softLimitOverridden && elapsed > m_softLimit) { - if (fractionComplete > 0.6) { - // If we're significantly more than half way by the - // time we reach the soft limit, ignore it (though - // always respect the hard limit, above). Otherwise - // respect the soft limit and report out of time now. - m_softLimitOverridden = true; - } else { - return true; - } - } + if (!m_haveLimits || fractionComplete < m_minFraction) { + return false; + } + + auto t = std::chrono::steady_clock::now(); + double elapsed = std::chrono::duration(t - m_start).count(); + + if (elapsed > m_hardLimit) { + return true; + } else if (!m_softLimitOverridden && elapsed > m_softLimit) { + if (fractionComplete > 0.6) { + // If we're significantly more than half way by the + // time we reach the soft limit, ignore it (though + // always respect the hard limit, above). Otherwise + // respect the soft limit and report out of time now. + m_softLimitOverridden = true; + } else { + return true; + } + } - return false; + return false; + } + + double secondsPerItem(int itemsRendered) const { + + if (itemsRendered == 0) return 0.0; + + auto t = std::chrono::steady_clock::now(); + double elapsed = std::chrono::duration(t - m_start).count(); + + return elapsed / itemsRendered; } private: std::chrono::time_point m_start; bool m_haveLimits; - double m_minFraction; - double m_softLimit; - double m_hardLimit; + double m_minFraction; // proportion, 0.0 -> 1.0 + double m_softLimit; // seconds + double m_hardLimit; // seconds bool m_softLimitOverridden; }; diff -r 57d192e26331 -r 13d9b422f7fe layer/ScrollableImageCache.cpp --- a/layer/ScrollableImageCache.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ScrollableImageCache.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -28,43 +28,43 @@ static HitCount count("ScrollableImageCache: scrolling"); int dx = (v->getXForFrame(m_startFrame) - - v->getXForFrame(newStartFrame)); + v->getXForFrame(newStartFrame)); #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE cerr << "ScrollableImageCache::scrollTo: start frame " << m_startFrame - << " -> " << newStartFrame << ", dx = " << dx << endl; + << " -> " << newStartFrame << ", dx = " << dx << endl; #endif if (m_startFrame == newStartFrame) { - // haven't moved + // haven't moved count.hit(); return; } - + m_startFrame = newStartFrame; - + if (!isValid()) { count.miss(); - return; + return; } int w = m_image.width(); if (dx == 0) { - // haven't moved visibly (even though start frame may have changed) + // haven't moved visibly (even though start frame may have changed) count.hit(); - return; + return; } if (dx <= -w || dx >= w) { - // scrolled entirely off - invalidate(); + // scrolled entirely off + invalidate(); count.miss(); - return; + return; } count.partial(); - + // dx is in range, cache is scrollable int dxp = dx; @@ -72,38 +72,38 @@ int copylen = (w - dxp) * int(sizeof(QRgb)); for (int y = 0; y < m_image.height(); ++y) { - QRgb *line = (QRgb *)m_image.scanLine(y); - if (dx < 0) { - memmove(line, line + dxp, copylen); - } else { - memmove(line + dxp, line, copylen); - } + QRgb *line = (QRgb *)m_image.scanLine(y); + if (dx < 0) { + memmove(line, line + dxp, copylen); + } else { + memmove(line + dxp, line, copylen); + } } - + // update valid area int px = m_validLeft; int pw = m_validWidth; - + px += dx; - + if (dx < 0) { - // we scrolled left - if (px < 0) { - pw += px; - px = 0; - if (pw < 0) { - pw = 0; - } - } + // we scrolled left + if (px < 0) { + pw += px; + px = 0; + if (pw < 0) { + pw = 0; + } + } } else { - // we scrolled right - if (px + pw > w) { - pw = w - px; - if (pw < 0) { - pw = 0; - } - } + // we scrolled right + if (px + pw > w) { + pw = w - px; + if (pw < 0) { + pw = 0; + } + } } m_validLeft = px; @@ -112,7 +112,7 @@ void ScrollableImageCache::adjustToTouchValidArea(int &left, int &width, - bool &isLeftOfValidArea) const + bool &isLeftOfValidArea) const { #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE cerr << "ScrollableImageCache::adjustToTouchValidArea: left " << left @@ -121,18 +121,18 @@ << ", width " << m_validWidth << " so right " << (m_validLeft + m_validWidth) << endl; #endif if (left < m_validLeft) { - isLeftOfValidArea = true; - if (left + width <= m_validLeft + m_validWidth) { - width = m_validLeft - left; - } + isLeftOfValidArea = true; + if (left + width <= m_validLeft + m_validWidth) { + width = m_validLeft - left; + } #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE cerr << "ScrollableImageCache: we're left of valid area, adjusted width to " << width << endl; #endif } else { - isLeftOfValidArea = false; - width = left + width - (m_validLeft + m_validWidth); - left = m_validLeft + m_validWidth; - if (width < 0) width = 0; + isLeftOfValidArea = false; + width = left + width - (m_validLeft + m_validWidth); + left = m_validLeft + m_validWidth; + if (width < 0) width = 0; #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE cerr << "ScrollableImageCache: we're right of valid area, adjusted left to " << left << ", width to " << width << endl; #endif @@ -141,80 +141,80 @@ void ScrollableImageCache::drawImage(int left, - int width, - QImage image, - int imageLeft, - int imageWidth) + int width, + QImage image, + int imageLeft, + int imageWidth) { if (image.height() != m_image.height()) { - cerr << "ScrollableImageCache::drawImage: ERROR: Supplied image height " - << image.height() << " does not match cache height " - << m_image.height() << endl; - throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage"); + cerr << "ScrollableImageCache::drawImage: ERROR: Supplied image height " + << image.height() << " does not match cache height " + << m_image.height() << endl; + throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage"); } if (left < 0 || width < 0 || left + width > m_image.width()) { - cerr << "ScrollableImageCache::drawImage: ERROR: Target area (left = " - << left << ", width = " << width << ", so right = " << left + width + cerr << "ScrollableImageCache::drawImage: ERROR: Target area (left = " + << left << ", width = " << width << ", so right = " << left + width << ") out of bounds for cache of width " << m_image.width() << endl; - throw std::logic_error("Target area out of bounds in ScrollableImageCache::drawImage"); + throw std::logic_error("Target area out of bounds in ScrollableImageCache::drawImage"); } if (imageLeft < 0 || imageWidth < 0 || - imageLeft + imageWidth > image.width()) { - cerr << "ScrollableImageCache::drawImage: ERROR: Source area (left = " - << imageLeft << ", width = " << imageWidth << ", so right = " + imageLeft + imageWidth > image.width()) { + cerr << "ScrollableImageCache::drawImage: ERROR: Source area (left = " + << imageLeft << ", width = " << imageWidth << ", so right = " << imageLeft + imageWidth << ") out of bounds for image of " - << "width " << image.width() << endl; - throw std::logic_error("Source area out of bounds in ScrollableImageCache::drawImage"); + << "width " << image.width() << endl; + throw std::logic_error("Source area out of bounds in ScrollableImageCache::drawImage"); } - + QPainter painter(&m_image); painter.drawImage(QRect(left, 0, width, m_image.height()), - image, - QRect(imageLeft, 0, imageWidth, image.height())); + image, + QRect(imageLeft, 0, imageWidth, image.height())); painter.end(); if (!isValid()) { - m_validLeft = left; - m_validWidth = width; - return; + m_validLeft = left; + m_validWidth = width; + return; } - + if (left < m_validLeft) { - if (left + width > m_validLeft + m_validWidth) { - // new image completely contains the old valid area -- - // use the new area as is - m_validLeft = left; - m_validWidth = width; - } else if (left + width < m_validLeft) { - // new image completely off left of old valid area -- - // we can't extend the valid area because the bit in - // between is not valid, so must use the new area only - m_validLeft = left; - m_validWidth = width; - } else { - // new image overlaps old valid area on left side -- - // use new left edge, and extend width to existing - // right edge - m_validWidth = (m_validLeft + m_validWidth) - left; - m_validLeft = left; - } + if (left + width > m_validLeft + m_validWidth) { + // new image completely contains the old valid area -- + // use the new area as is + m_validLeft = left; + m_validWidth = width; + } else if (left + width < m_validLeft) { + // new image completely off left of old valid area -- + // we can't extend the valid area because the bit in + // between is not valid, so must use the new area only + m_validLeft = left; + m_validWidth = width; + } else { + // new image overlaps old valid area on left side -- + // use new left edge, and extend width to existing + // right edge + m_validWidth = (m_validLeft + m_validWidth) - left; + m_validLeft = left; + } } else { - if (left > m_validLeft + m_validWidth) { - // new image completely off right of old valid area -- - // we can't extend the valid area because the bit in - // between is not valid, so must use the new area only - m_validLeft = left; - m_validWidth = width; - } else if (left + width > m_validLeft + m_validWidth) { - // new image overlaps old valid area on right side -- - // use existing left edge, and extend width to new - // right edge - m_validWidth = (left + width) - m_validLeft; - // (m_validLeft unchanged) - } else { - // new image completely contained within old valid - // area -- leave the old area unchanged - } + if (left > m_validLeft + m_validWidth) { + // new image completely off right of old valid area -- + // we can't extend the valid area because the bit in + // between is not valid, so must use the new area only + m_validLeft = left; + m_validWidth = width; + } else if (left + width > m_validLeft + m_validWidth) { + // new image overlaps old valid area on right side -- + // use existing left edge, and extend width to new + // right edge + m_validWidth = (left + width) - m_validLeft; + // (m_validLeft unchanged) + } else { + // new image completely contained within old valid + // area -- leave the old area unchanged + } } } diff -r 57d192e26331 -r 13d9b422f7fe layer/ScrollableImageCache.h --- a/layer/ScrollableImageCache.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ScrollableImageCache.h Mon Sep 17 13:51:31 2018 +0100 @@ -38,22 +38,22 @@ { public: ScrollableImageCache() : - m_validLeft(0), - m_validWidth(0), - m_startFrame(0), - m_zoomLevel(0) + m_validLeft(0), + m_validWidth(0), + m_startFrame(0), + m_zoomLevel(0) {} void invalidate() { - m_validWidth = 0; + m_validWidth = 0; } bool isValid() const { - return m_validWidth > 0; + return m_validWidth > 0; } QSize getSize() const { - return m_image.size(); + return m_image.size(); } /** @@ -66,25 +66,25 @@ invalidate(); } } - + int getValidLeft() const { - return m_validLeft; + return m_validLeft; } int getValidWidth() const { - return m_validWidth; + return m_validWidth; } int getValidRight() const { - return m_validLeft + m_validWidth; + return m_validLeft + m_validWidth; } QRect getValidArea() const { - return QRect(m_validLeft, 0, m_validWidth, m_image.height()); + return QRect(m_validLeft, 0, m_validWidth, m_image.height()); } int getZoomLevel() const { - return m_zoomLevel; + return m_zoomLevel; } /** @@ -101,7 +101,7 @@ } sv_frame_t getStartFrame() const { - return m_startFrame; + return m_startFrame; } /** @@ -118,7 +118,7 @@ } const QImage &getImage() const { - return m_image; + return m_image; } /** @@ -137,7 +137,7 @@ * modify anything about the cache, only about the arguments. */ void adjustToTouchValidArea(int &left, int &width, - bool &isLeftOfValidArea) const; + bool &isLeftOfValidArea) const; /** * Draw from an image onto the cache. The supplied image must have @@ -147,10 +147,10 @@ * the source region of the image. */ void drawImage(int left, - int width, - QImage image, - int imageLeft, - int imageWidth); + int width, + QImage image, + int imageLeft, + int imageWidth); private: QImage m_image; diff -r 57d192e26331 -r 13d9b422f7fe layer/ScrollableMagRangeCache.cpp --- a/layer/ScrollableMagRangeCache.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ScrollableMagRangeCache.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -23,20 +23,20 @@ void ScrollableMagRangeCache::scrollTo(const LayerGeometryProvider *v, - sv_frame_t newStartFrame) -{ + sv_frame_t newStartFrame) +{ static HitCount count("ScrollableMagRangeCache: scrolling"); int dx = (v->getXForFrame(m_startFrame) - - v->getXForFrame(newStartFrame)); + v->getXForFrame(newStartFrame)); #ifdef DEBUG_SCROLLABLE_MAG_RANGE_CACHE cerr << "ScrollableMagRangeCache::scrollTo: start frame " << m_startFrame - << " -> " << newStartFrame << ", dx = " << dx << endl; + << " -> " << newStartFrame << ", dx = " << dx << endl; #endif if (m_startFrame == newStartFrame) { - // haven't moved + // haven't moved count.hit(); return; } @@ -44,51 +44,51 @@ m_startFrame = newStartFrame; if (dx == 0) { - // haven't moved visibly (even though start frame may have changed) + // haven't moved visibly (even though start frame may have changed) count.hit(); - return; + return; } - + int w = int(m_ranges.size()); if (dx <= -w || dx >= w) { - // scrolled entirely off - invalidate(); + // scrolled entirely off + invalidate(); count.miss(); - return; + return; } count.partial(); - + // dx is in range, cache is scrollable if (dx < 0) { - // The new start frame is to the left of the old start - // frame. We need to add some empty ranges at the left (start) - // end and clip the right end. Assemble -dx new values, then - // w+dx old values starting at index 0. + // The new start frame is to the left of the old start + // frame. We need to add some empty ranges at the left (start) + // end and clip the right end. Assemble -dx new values, then + // w+dx old values starting at index 0. - auto newRanges = vector(-dx); - newRanges.insert(newRanges.end(), - m_ranges.begin(), m_ranges.begin() + (w + dx)); - m_ranges = newRanges; - + auto newRanges = vector(-dx); + newRanges.insert(newRanges.end(), + m_ranges.begin(), m_ranges.begin() + (w + dx)); + m_ranges = newRanges; + } else { - // The new start frame is to the right of the old start - // frame. We want to clip the left (start) end and add some - // empty ranges at the right end. Assemble w-dx old values - // starting at index dx, then dx new values. + // The new start frame is to the right of the old start + // frame. We want to clip the left (start) end and add some + // empty ranges at the right end. Assemble w-dx old values + // starting at index dx, then dx new values. - auto newRanges = vector(dx); - newRanges.insert(newRanges.begin(), - m_ranges.begin() + dx, m_ranges.end()); - m_ranges = newRanges; + auto newRanges = vector(dx); + newRanges.insert(newRanges.begin(), + m_ranges.begin() + dx, m_ranges.end()); + m_ranges = newRanges; } #ifdef DEBUG_SCROLLABLE_MAG_RANGE_CACHE cerr << "maxes (" << m_ranges.size() << ") now: "; for (int i = 0; in_range_for(m_ranges, i); ++i) { - cerr << m_ranges[i].getMax() << " "; + cerr << m_ranges[i].getMax() << " "; } cerr << endl; #endif @@ -102,7 +102,7 @@ cerr << "ScrollableMagRangeCache::getRange(" << x << ", " << count << ")" << endl; #endif for (int i = 0; i < count; ++i) { - r.sample(m_ranges.at(x + i)); + r.sample(m_ranges.at(x + i)); } return r; } @@ -111,12 +111,12 @@ ScrollableMagRangeCache::sampleColumn(int column, const MagnitudeRange &r) { if (!in_range_for(m_ranges, column)) { - cerr << "ERROR: ScrollableMagRangeCache::sampleColumn: column " << column - << " is out of range for cache of width " << m_ranges.size() - << " (with start frame " << m_startFrame << ")" << endl; - throw logic_error("column out of range"); + cerr << "ERROR: ScrollableMagRangeCache::sampleColumn: column " << column + << " is out of range for cache of width " << m_ranges.size() + << " (with start frame " << m_startFrame << ")" << endl; + throw logic_error("column out of range"); } else { - m_ranges[column].sample(r); + m_ranges[column].sample(r); } } diff -r 57d192e26331 -r 13d9b422f7fe layer/ScrollableMagRangeCache.h --- a/layer/ScrollableMagRangeCache.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/ScrollableMagRangeCache.h Mon Sep 17 13:51:31 2018 +0100 @@ -36,16 +36,16 @@ { public: ScrollableMagRangeCache() : - m_startFrame(0), - m_zoomLevel(0) + m_startFrame(0), + m_zoomLevel(0) {} void invalidate() { - m_ranges = std::vector(m_ranges.size()); + m_ranges = std::vector(m_ranges.size()); } int getWidth() const { - return int(m_ranges.size()); + return int(m_ranges.size()); } /** @@ -54,12 +54,12 @@ */ void resize(int newWidth) { if (getWidth() != newWidth) { - m_ranges = std::vector(newWidth); + m_ranges = std::vector(newWidth); } } - + int getZoomLevel() const { - return m_zoomLevel; + return m_zoomLevel; } /** @@ -76,7 +76,7 @@ } sv_frame_t getStartFrame() const { - return m_startFrame; + return m_startFrame; } /** @@ -93,21 +93,21 @@ } bool isColumnSet(int column) const { - return in_range_for(m_ranges, column) && m_ranges.at(column).isSet(); + return in_range_for(m_ranges, column) && m_ranges.at(column).isSet(); } bool areColumnsSet(int x, int count) const { - for (int i = 0; i < count; ++i) { - if (!isColumnSet(x + i)) return false; - } - return true; + for (int i = 0; i < count; ++i) { + if (!isColumnSet(x + i)) return false; + } + return true; } /** * Get the magnitude range for a single column. */ MagnitudeRange getRange(int column) const { - return m_ranges.at(column); + return m_ranges.at(column); } /** diff -r 57d192e26331 -r 13d9b422f7fe layer/SingleColourLayer.cpp --- a/layer/SingleColourLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SingleColourLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -103,7 +103,7 @@ val = m_colour; } else { - val = Layer::getPropertyRangeAndValue(name, min, max, deflt); + val = Layer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -111,7 +111,7 @@ QString SingleColourLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Colour") { ColourDatabase *db = ColourDatabase::getInstance(); @@ -283,7 +283,7 @@ s += QString("colourName=\"%1\" " "colour=\"%2\" " "darkBackground=\"%3\" ") - .arg(colourName) + .arg(colourName) .arg(colourSpec) .arg(darkbg); diff -r 57d192e26331 -r 13d9b422f7fe layer/SingleColourLayer.h --- a/layer/SingleColourLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SingleColourLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SINGLE_COLOUR_LAYER_H_ -#define _SINGLE_COLOUR_LAYER_H_ +#ifndef SV_SINGLE_COLOUR_LAYER_H +#define SV_SINGLE_COLOUR_LAYER_H #include "Layer.h" #include @@ -62,7 +62,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; virtual void setProperty(const PropertyName &, int value); diff -r 57d192e26331 -r 13d9b422f7fe layer/SliceLayer.cpp --- a/layer/SliceLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SliceLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -31,15 +31,17 @@ SliceLayer::SliceLayer() : m_sliceableModel(0), - m_colourMap(0), + m_colourMap(int(ColourMapper::Ice)), m_energyScale(dBScale), m_samplingMode(SampleMean), - m_plotStyle(PlotSteps), + m_plotStyle(PlotLines), m_binScale(LinearBins), m_normalize(false), m_threshold(0.0), m_initialThreshold(0.0), m_gain(1.0), + m_minbin(0), + m_maxbin(0), m_currentf0(0), m_currentf1(0) { @@ -65,9 +67,15 @@ m_sliceableModel = sliceable; + if (!m_sliceableModel) return; + connectSignals(m_sliceableModel); + m_minbin = 0; + m_maxbin = m_sliceableModel->getHeight(); + emit modelReplaced(); + emit layerParametersChanged(); } void @@ -107,13 +115,10 @@ maxbin = 0; if (!m_sliceableModel) return ""; - int xorigin = m_xorigins[v]; - int w = v->getPaintWidth() - xorigin - 1; - + minbin = int(getBinForX(v, p.x())); + maxbin = int(getBinForX(v, p.x() + 1)); + int mh = m_sliceableModel->getHeight(); - minbin = getBinForX(p.x() - xorigin, mh, w); - maxbin = getBinForX(p.x() - xorigin + 1, mh, w); - if (minbin >= mh) minbin = mh - 1; if (maxbin >= mh) maxbin = mh - 1; if (minbin < 0) minbin = 0; @@ -133,12 +138,15 @@ if (includeBinDescription) { + int i0 = minbin - m_minbin; + int i1 = maxbin - m_minbin; + float minvalue = 0.0; - if (minbin < int(m_values.size())) minvalue = m_values[minbin]; + if (in_range_for(m_values, i0)) minvalue = m_values[i0]; float maxvalue = minvalue; - if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; - + if (in_range_for(m_values, i1)) maxvalue = m_values[i1]; + if (minvalue > maxvalue) std::swap(minvalue, maxvalue); QString binstr; @@ -180,10 +188,21 @@ } double -SliceLayer::getXForBin(int bin, int count, double w) const +SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const { double x = 0; + bin -= m_minbin; + if (bin < 0) bin = 0; + + double count = m_maxbin - m_minbin; + if (count < 0) count = 0; + + int pw = v->getPaintWidth(); + int origin = m_xorigins[v->getId()]; + int w = pw - origin; + if (w < 1) w = 1; + switch (m_binScale) { case LinearBins: @@ -191,51 +210,78 @@ break; case LogBins: - x = (w * log10(bin + 1)) / log10(count + 1); + // The 0.8 here is an awkward compromise. Our x-coord is + // proportional to log of bin number, with the x-coord "of a + // bin" being that of the left edge of the bin range. We can't + // start counting bins from 0, as that would give us x = -Inf + // and hide the first bin entirely. But if we start from 1, we + // are giving a lot of space to the first bin, which in most + // display modes won't be used because the "point" location + // for that bin is in the middle of it. Yet in some modes + // we'll still want it. A compromise is to count our first bin + // as "a bit less than 1", so that most of it is visible but a + // bit is tactfully cropped at the left edge so it doesn't + // take up so much space. + x = (w * log10(bin + 0.8)) / log10(count + 0.8); break; case InvertedLogBins: x = w - (w * log10(count - bin - 1)) / log10(count); break; } - - return x; + + return x + origin; } -int -SliceLayer::getBinForX(double x, int count, double w) const +double +SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const { - int bin = 0; + double bin = 0; + double count = m_maxbin - m_minbin; + if (count < 0) count = 0; + + int pw = v->getPaintWidth(); + int origin = m_xorigins[v->getId()]; + + int w = pw - origin; + if (w < 1) w = 1; + + x = x - origin; + if (x < 0) x = 0; + + double eps = 1e-10; + switch (m_binScale) { case LinearBins: - bin = int((x * count) / w + 0.0001); + bin = (x * count) / w + eps; break; case LogBins: - bin = int(pow(10.0, (x * log10(count + 1)) / w) - 1 + 0.0001); + // See comment in getXForBin + bin = pow(10.0, (x * log10(count + 0.8)) / w) - 0.8 + eps; break; case InvertedLogBins: - bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001); + bin = count + 1 - pow(10.0, (log10(count) * (w - x)) / double(w)) + eps; break; } - return bin; + return bin + m_minbin; } double -SliceLayer::getYForValue(double value, const LayerGeometryProvider *v, double &norm) const +SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const { norm = 0.0; - if (m_yorigins.find(v) == m_yorigins.end()) return 0; + if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0; value *= m_gain; - int yorigin = m_yorigins[v]; - int h = m_heights[v]; + int yorigin = m_yorigins[v->getId()]; + int h = m_heights[v->getId()]; double thresh = getThresholdDb(); double y = 0.0; @@ -262,7 +308,9 @@ case AbsoluteScale: value = fabs(value); - // and fall through +#if (__GNUC__ >= 7) + __attribute__ ((fallthrough)); +#endif case LinearScale: default: @@ -276,14 +324,14 @@ } double -SliceLayer::getValueForY(double y, const LayerGeometryProvider *v) const +SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const { double value = 0.0; - if (m_yorigins.find(v) == m_yorigins.end()) return value; + if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value; - int yorigin = m_yorigins[v]; - int h = m_heights[v]; + int yorigin = m_yorigins[v->getId()]; + int h = m_heights[v->getId()]; double thresh = getThresholdDb(); if (h <= 0) return value; @@ -325,31 +373,43 @@ if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) { if (!m_scalePoints.empty()) { paint.setPen(QColor(240, 240, 240)); //!!! and dark background? + int ratio = int(round(double(v->getPaintHeight()) / + m_scalePaintHeight)); for (int i = 0; i < (int)m_scalePoints.size(); ++i) { - paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]); + paint.drawLine(0, m_scalePoints[i] * ratio, + rect.width(), m_scalePoints[i] * ratio); } } } - paint.setPen(getBaseQColor()); + if (m_plotStyle == PlotBlocks) { + // Must use actual zero-width pen, too slow otherwise + paint.setPen(QPen(getBaseQColor(), 0)); + } else { + paint.setPen(PaintAssistant::scalePen(getBaseQColor())); + } int xorigin = getVerticalScaleWidth(v, true, paint) + 1; - int w = v->getPaintWidth() - xorigin - 1; - - m_xorigins[v] = xorigin; // for use in getFeatureDescription + m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; int h = yorigin - paint.fontMetrics().height() - 8; - m_yorigins[v] = yorigin; // for getYForValue etc - m_heights[v] = h; + m_yorigins[v->getId()] = yorigin; // for getYForValue etc + m_heights[v->getId()] = h; if (h <= 0) return; QPainterPath path; int mh = m_sliceableModel->getHeight(); + int bin0 = 0; + if (m_maxbin > m_minbin) { + mh = m_maxbin - m_minbin; + bin0 = m_minbin; + } + int divisor = 0; m_values.clear(); @@ -383,7 +443,7 @@ for (int col = col0; col <= col1; ++col) { for (int bin = 0; bin < mh; ++bin) { - float value = m_sliceableModel->getValueAt(col, bin); + float value = m_sliceableModel->getValueAt(col, bin0 + bin); if (bin < cs) value *= curve[bin]; if (m_samplingMode == SamplePeak) { if (value > m_values[bin]) m_values[bin] = value; @@ -407,25 +467,25 @@ } } - double nx = xorigin; + double nx = getXForBin(v, bin0); ColourMapper mapper(m_colourMap, 0, 1); for (int bin = 0; bin < mh; ++bin) { double x = nx; - nx = xorigin + getXForBin(bin + 1, mh, w); + nx = getXForBin(v, bin + bin0 + 1); double value = m_values[bin]; double norm = 0.0; - double y = getYForValue(value, v, norm); + double y = getYForValue(v, value, norm); if (m_plotStyle == PlotLines) { if (bin == 0) { - path.moveTo(x, y); + path.moveTo((x + nx) / 2, y); } else { - path.lineTo(x, y); + path.lineTo((x + nx) / 2, y); } } else if (m_plotStyle == PlotSteps) { @@ -456,60 +516,20 @@ paint.drawPath(path); } paint.restore(); -/* - QPoint discard; - - if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() && - v->shouldIlluminateLocalFeatures(this, discard)) { - - int sampleRate = m_sliceableModel->getSampleRate(); - - QString startText = QString("%1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f0, sampleRate).toText(true))) - .arg(f0); - - QString endText = QString(" %1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f1, sampleRate).toText(true))) - .arg(f1); - - QString durationText = QString("(%1 / %2) ") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f1 - f0 + 1, sampleRate).toText(true))) - .arg(f1 - f0 + 1); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + 5, - startText, PaintAssistant::OutlinedText); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + paint.fontMetrics().height() + 10, - endText, PaintAssistant::OutlinedText); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + 2*paint.fontMetrics().height() + 15, - durationText, PaintAssistant::OutlinedText); - } -*/ } int SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { + int width; if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) { - return std::max(paint.fontMetrics().width("0.0") + 13, - paint.fontMetrics().width("x10-10")); + width = std::max(paint.fontMetrics().width("0.0") + 13, + paint.fontMetrics().width("x10-10")); } else { - return std::max(paint.fontMetrics().width(tr("0dB")), - paint.fontMetrics().width(tr("-Inf"))) + 13; + width = std::max(paint.fontMetrics().width(tr("0dB")), + paint.fontMetrics().width(tr("-Inf"))) + 13; } + return width; } void @@ -537,6 +557,15 @@ mult, const_cast *>(&m_scalePoints)); + // Ugly hack (but then everything about this scale drawing is a + // bit ugly). In pixel-doubling hi-dpi scenarios, the scale is + // painted at pixel-doubled resolution but we do explicit + // pixel-doubling ourselves when painting the layer content. We + // make a note of this here so that we can compare with the + // equivalent dimension in the paint method when deciding where to + // place scale continuation lines. + m_scalePaintHeight = v->getPaintHeight(); + if (mult != 1 && mult != 0) { int log = int(lrint(log10(mult))); QString a = tr("x10"); @@ -547,6 +576,17 @@ } } +bool +SliceLayer::hasLightBackground() const +{ + if (usesSolidColour()) { + ColourMapper mapper(m_colourMap, 0, 1); + return mapper.hasLightBackground(); + } else { + return SingleColourLayer::hasLightBackground(); + } +} + Layer::PropertyList SliceLayer::getProperties() const { @@ -591,7 +631,7 @@ if (name == "Scale") return ValueProperty; if (name == "Sampling Mode") return ValueProperty; if (name == "Bin Scale") return ValueProperty; - if (name == "Colour" && m_plotStyle == PlotFilledBlocks) return ValueProperty; + if (name == "Colour" && usesSolidColour()) return ColourMapProperty; return SingleColourLayer::getPropertyType(name); } @@ -621,57 +661,57 @@ if (name == "Gain") { - *min = -50; - *max = 50; + *min = -50; + *max = 50; *deflt = 0; - cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; +// cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; - val = int(lrint(log10(m_gain) * 20.0)); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(log10(m_gain) * 20.0)); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Threshold") { - *min = -80; - *max = 0; + *min = -80; + *max = 0; *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); - if (*deflt < *min) *deflt = *min; - if (*deflt > *max) *deflt = *max; + if (*deflt < *min) *deflt = *min; + if (*deflt > *max) *deflt = *max; - val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Normalize") { - - val = (m_normalize ? 1 : 0); + + val = (m_normalize ? 1 : 0); *deflt = 0; - } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + } else if (name == "Colour" && usesSolidColour()) { *min = 0; *max = ColourMapper::getColourMapCount() - 1; - *deflt = 0; + *deflt = int(ColourMapper::Ice); val = m_colourMap; } else if (name == "Scale") { - *min = 0; - *max = 3; + *min = 0; + *max = 3; *deflt = (int)dBScale; - val = (int)m_energyScale; + val = (int)m_energyScale; } else if (name == "Sampling Mode") { - *min = 0; - *max = 2; + *min = 0; + *max = 2; *deflt = (int)SampleMean; - val = (int)m_samplingMode; + val = (int)m_samplingMode; } else if (name == "Plot Type") { @@ -691,7 +731,7 @@ val = (int)m_binScale; } else { - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -699,44 +739,44 @@ QString SliceLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { - if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + if (name == "Colour" && usesSolidColour()) { return ColourMapper::getColourMapName(value); } if (name == "Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Meter"); - case 2: return tr("Log"); - case 3: return tr("Absolute"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Meter"); + case 2: return tr("Log"); + case 3: return tr("Absolute"); + } } if (name == "Sampling Mode") { - switch (value) { - default: - case 0: return tr("Any"); - case 1: return tr("Mean"); - case 2: return tr("Peak"); - } + switch (value) { + default: + case 0: return tr("Any"); + case 1: return tr("Mean"); + case 2: return tr("Peak"); + } } if (name == "Plot Type") { - switch (value) { - default: - case 0: return tr("Lines"); - case 1: return tr("Steps"); - case 2: return tr("Blocks"); - case 3: return tr("Colours"); - } + switch (value) { + default: + case 0: return tr("Lines"); + case 1: return tr("Steps"); + case 2: return tr("Blocks"); + case 3: return tr("Colours"); + } } if (name == "Bin Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Log"); - case 2: return tr("Rev Log"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + case 2: return tr("Rev Log"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -757,38 +797,38 @@ SliceLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(powf(10, float(value)/20.0f)); + setGain(powf(10, float(value)/20.0f)); } else if (name == "Threshold") { - if (value == -80) setThreshold(0.0f); - else setThreshold(float(AudioLevel::dB_to_multiplier(value))); - } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + if (value == -80) setThreshold(0.0f); + else setThreshold(float(AudioLevel::dB_to_multiplier(value))); + } else if (name == "Colour" && usesSolidColour()) { setFillColourMap(value); } else if (name == "Scale") { - switch (value) { - default: - case 0: setEnergyScale(LinearScale); break; - case 1: setEnergyScale(MeterScale); break; - case 2: setEnergyScale(dBScale); break; - case 3: setEnergyScale(AbsoluteScale); break; - } + switch (value) { + default: + case 0: setEnergyScale(LinearScale); break; + case 1: setEnergyScale(MeterScale); break; + case 2: setEnergyScale(dBScale); break; + case 3: setEnergyScale(AbsoluteScale); break; + } } else if (name == "Plot Type") { - setPlotStyle(PlotStyle(value)); + setPlotStyle(PlotStyle(value)); } else if (name == "Sampling Mode") { - switch (value) { - default: - case 0: setSamplingMode(NearestSample); break; - case 1: setSamplingMode(SampleMean); break; - case 2: setSamplingMode(SamplePeak); break; - } + switch (value) { + default: + case 0: setSamplingMode(NearestSample); break; + case 1: setSamplingMode(SampleMean); break; + case 2: setSamplingMode(SamplePeak); break; + } } else if (name == "Bin Scale") { - switch (value) { - default: - case 0: setBinScale(LinearBins); break; - case 1: setBinScale(LogBins); break; - case 2: setBinScale(InvertedLogBins); break; - } + switch (value) { + default: + case 0: setBinScale(LinearBins); break; + case 1: setBinScale(LogBins); break; + case 2: setBinScale(InvertedLogBins); break; + } } else if (name == "Normalize") { - setNormalize(value ? true : false); + setNormalize(value ? true : false); } else { SingleColourLayer::setProperty(name, value); } @@ -886,21 +926,25 @@ QString s; s += QString("colourScheme=\"%1\" " - "energyScale=\"%2\" " + "energyScale=\"%2\" " "samplingMode=\"%3\" " "plotStyle=\"%4\" " "binScale=\"%5\" " "gain=\"%6\" " "threshold=\"%7\" " - "normalize=\"%8\"") + "normalize=\"%8\" %9") .arg(m_colourMap) - .arg(m_energyScale) + .arg(m_energyScale) .arg(m_samplingMode) .arg(m_plotStyle) .arg(m_binScale) .arg(m_gain) .arg(m_threshold) - .arg(m_normalize ? "true" : "false"); + .arg(m_normalize ? "true" : "false") + .arg(QString("minbin=\"%1\" " + "maxbin=\"%2\"") + .arg(m_minbin) + .arg(m_maxbin)); SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s); } @@ -913,22 +957,22 @@ SingleColourLayer::setProperties(attributes); EnergyScale scale = (EnergyScale) - attributes.value("energyScale").toInt(&ok); + attributes.value("energyScale").toInt(&ok); if (ok) setEnergyScale(scale); SamplingMode mode = (SamplingMode) - attributes.value("samplingMode").toInt(&ok); + attributes.value("samplingMode").toInt(&ok); if (ok) setSamplingMode(mode); int colourMap = attributes.value("colourScheme").toInt(&ok); if (ok) setFillColourMap(colourMap); PlotStyle s = (PlotStyle) - attributes.value("plotStyle").toInt(&ok); + attributes.value("plotStyle").toInt(&ok); if (ok) setPlotStyle(s); BinScale b = (BinScale) - attributes.value("binScale").toInt(&ok); + attributes.value("binScale").toInt(&ok); if (ok) setBinScale(b); float gain = attributes.value("gain").toFloat(&ok); @@ -939,11 +983,105 @@ bool normalize = (attributes.value("normalize").trimmed() == "true"); setNormalize(normalize); + + bool alsoOk = false; + + float min = attributes.value("minbin").toFloat(&ok); + float max = attributes.value("maxbin").toFloat(&alsoOk); + if (ok && alsoOk) setDisplayExtents(min, max); } bool -SliceLayer::getValueExtents(double &, double &, bool &, QString &) const +SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic, + QString &unit) const { - return false; + if (!m_sliceableModel) return false; + + min = 0; + max = double(m_sliceableModel->getHeight()); + + logarithmic = (m_binScale == BinScale::LogBins); + unit = ""; + + return true; } +bool +SliceLayer::getDisplayExtents(double &min, double &max) const +{ + if (!m_sliceableModel) return false; + + double hmax = double(m_sliceableModel->getHeight()); + + min = m_minbin; + max = m_maxbin; + if (max <= min) { + min = 0; + max = hmax; + } + if (min < 0) min = 0; + if (max > hmax) max = hmax; + + return true; +} + +bool +SliceLayer::setDisplayExtents(double min, double max) +{ + if (!m_sliceableModel) return false; + + m_minbin = int(lrint(min)); + m_maxbin = int(lrint(max)); + + emit layerParametersChanged(); + return true; +} + +int +SliceLayer::getVerticalZoomSteps(int &defaultStep) const +{ + if (!m_sliceableModel) return 0; + + defaultStep = 0; + int h = m_sliceableModel->getHeight(); + return h; +} + +int +SliceLayer::getCurrentVerticalZoomStep() const +{ + if (!m_sliceableModel) return 0; + + double min, max; + getDisplayExtents(min, max); + return m_sliceableModel->getHeight() - int(lrint(max - min)); +} + +void +SliceLayer::setVerticalZoomStep(int step) +{ + if (!m_sliceableModel) return; + +// SVDEBUG << "SliceLayer::setVerticalZoomStep(" < m_scalePoints; - mutable std::map m_xorigins; - mutable std::map m_yorigins; - mutable std::map m_heights; + mutable int m_scalePaintHeight; + mutable std::map m_xorigins; // LayerGeometryProvider id -> x + mutable std::map m_yorigins; // LayerGeometryProvider id -> y + mutable std::map m_heights; // LayerGeometryProvider id -> h mutable sv_frame_t m_currentf0; mutable sv_frame_t m_currentf1; mutable std::vector m_values; diff -r 57d192e26331 -r 13d9b422f7fe layer/SpectrogramLayer.cpp --- a/layer/SpectrogramLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SpectrogramLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -25,6 +25,8 @@ #include "base/LogRange.h" #include "base/ColumnOp.h" #include "base/Strings.h" +#include "base/StorageAdviser.h" +#include "base/Exceptions.h" #include "widgets/CommandHistory.h" #include "data/model/Dense3DModelPeakCache.h" @@ -80,6 +82,7 @@ m_haveDetailedScale(false), m_exiting(false), m_fftModel(0), + m_wholeCache(0), m_peakCache(0), m_peakCacheDivisor(8) { @@ -90,26 +93,26 @@ m_initialMaxFrequency = 0; setMaxFrequency(0); } else if (config == MelodicRange) { - setWindowSize(8192); - setWindowHopLevel(4); + setWindowSize(8192); + setWindowHopLevel(4); m_initialMaxFrequency = 1500; - setMaxFrequency(1500); + setMaxFrequency(1500); setMinFrequency(40); - setColourScale(ColourScaleType::Linear); + setColourScale(ColourScaleType::Linear); setColourMap(ColourMapper::Sunset); setBinScale(BinScale::Log); colourConfigName = "spectrogram-melodic-colour"; colourConfigDefault = int(ColourMapper::Sunset); // setGain(20); } else if (config == MelodicPeaks) { - setWindowSize(4096); - setWindowHopLevel(5); + setWindowSize(4096); + setWindowHopLevel(5); m_initialMaxFrequency = 2000; - setMaxFrequency(2000); - setMinFrequency(40); - setBinScale(BinScale::Log); - setColourScale(ColourScaleType::Linear); - setBinDisplay(BinDisplay::PeakFrequencies); + setMaxFrequency(2000); + setMinFrequency(40); + setBinScale(BinScale::Log); + setColourScale(ColourScaleType::Linear); + setBinDisplay(BinDisplay::PeakFrequencies); setNormalization(ColumnNormalization::Max1); colourConfigName = "spectrogram-melodic-colour"; colourConfigDefault = int(ColourMapper::Sunset); @@ -129,7 +132,23 @@ SpectrogramLayer::~SpectrogramLayer() { invalidateRenderers(); - invalidateFFTModel(); + deleteDerivedModels(); +} + +void +SpectrogramLayer::deleteDerivedModels() +{ + if (m_fftModel) m_fftModel->aboutToDelete(); + if (m_peakCache) m_peakCache->aboutToDelete(); + if (m_wholeCache) m_wholeCache->aboutToDelete(); + + delete m_fftModel; + delete m_peakCache; + delete m_wholeCache; + + m_fftModel = 0; + m_peakCache = 0; + m_wholeCache = 0; } pair @@ -181,6 +200,7 @@ case ColumnNormalization::Hybrid: return 3; case ColumnNormalization::Sum1: + case ColumnNormalization::Range01: default: return 0; } } @@ -193,7 +213,8 @@ if (model == m_model) return; m_model = model; - invalidateFFTModel(); + + recreateFFTModel(); if (!m_model || !m_model->isOK()) return; @@ -201,7 +222,7 @@ connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); + this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); emit modelReplaced(); } @@ -255,6 +276,7 @@ if (name == "Gain") return RangeProperty; if (name == "Colour Rotation") return RangeProperty; if (name == "Threshold") return RangeProperty; + if (name == "Colour") return ColourMapProperty; return ValueProperty; } @@ -264,19 +286,19 @@ if (name == "Bin Display" || name == "Frequency Scale") return tr("Bins"); if (name == "Window Size" || - name == "Window Increment") return tr("Window"); + name == "Window Increment") return tr("Window"); if (name == "Colour" || - name == "Threshold" || - name == "Colour Rotation") return tr("Colour"); + name == "Threshold" || + name == "Colour Rotation") return tr("Colour"); if (name == "Normalization" || name == "Gain" || - name == "Colour Scale") return tr("Scale"); + name == "Colour Scale") return tr("Scale"); return QString(); } int SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, - int *min, int *max, int *deflt) const + int *min, int *max, int *deflt) const { int val = 0; @@ -287,127 +309,127 @@ if (name == "Gain") { - *min = -50; - *max = 50; + *min = -50; + *max = 50; *deflt = int(lrint(log10(m_initialGain) * 20.0)); - if (*deflt < *min) *deflt = *min; - if (*deflt > *max) *deflt = *max; - - val = int(lrint(log10(m_gain) * 20.0)); - if (val < *min) val = *min; - if (val > *max) val = *max; + if (*deflt < *min) *deflt = *min; + if (*deflt > *max) *deflt = *max; + + val = int(lrint(log10(m_gain) * 20.0)); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Threshold") { - *min = -81; - *max = -1; + *min = -81; + *max = -1; *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); - if (*deflt < *min) *deflt = *min; - if (*deflt > *max) *deflt = *max; - - val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); - if (val < *min) val = *min; - if (val > *max) val = *max; + if (*deflt < *min) *deflt = *min; + if (*deflt > *max) *deflt = *max; + + val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Colour Rotation") { - *min = 0; - *max = 256; + *min = 0; + *max = 256; *deflt = m_initialRotation; - val = m_colourRotation; + val = m_colourRotation; } else if (name == "Colour Scale") { // linear, meter, db^2, db, phase - *min = 0; - *max = 4; + *min = 0; + *max = 4; *deflt = 2; - val = convertFromColourScale(m_colourScale, m_colourScaleMultiple); + val = convertFromColourScale(m_colourScale, m_colourScaleMultiple); } else if (name == "Colour") { - *min = 0; - *max = ColourMapper::getColourMapCount() - 1; + *min = 0; + *max = ColourMapper::getColourMapCount() - 1; *deflt = 0; - val = m_colourMap; + val = m_colourMap; } else if (name == "Window Size") { - *min = 0; - *max = 10; + *min = 0; + *max = 10; *deflt = 5; - - val = 0; - int ws = m_windowSize; - while (ws > 32) { ws >>= 1; val ++; } + + val = 0; + int ws = m_windowSize; + while (ws > 32) { ws >>= 1; val ++; } } else if (name == "Window Increment") { - - *min = 0; - *max = 5; + + *min = 0; + *max = 5; *deflt = 2; val = m_windowHopLevel; } else if (name == "Min Frequency") { - *min = 0; - *max = 9; + *min = 0; + *max = 9; *deflt = 1; - switch (m_minFrequency) { - case 0: default: val = 0; break; - case 10: val = 1; break; - case 20: val = 2; break; - case 40: val = 3; break; - case 100: val = 4; break; - case 250: val = 5; break; - case 500: val = 6; break; - case 1000: val = 7; break; - case 4000: val = 8; break; - case 10000: val = 9; break; - } + switch (m_minFrequency) { + case 0: default: val = 0; break; + case 10: val = 1; break; + case 20: val = 2; break; + case 40: val = 3; break; + case 100: val = 4; break; + case 250: val = 5; break; + case 500: val = 6; break; + case 1000: val = 7; break; + case 4000: val = 8; break; + case 10000: val = 9; break; + } } else if (name == "Max Frequency") { - *min = 0; - *max = 9; + *min = 0; + *max = 9; *deflt = 6; - switch (m_maxFrequency) { - case 500: val = 0; break; - case 1000: val = 1; break; - case 1500: val = 2; break; - case 2000: val = 3; break; - case 4000: val = 4; break; - case 6000: val = 5; break; - case 8000: val = 6; break; - case 12000: val = 7; break; - case 16000: val = 8; break; - default: val = 9; break; - } + switch (m_maxFrequency) { + case 500: val = 0; break; + case 1000: val = 1; break; + case 1500: val = 2; break; + case 2000: val = 3; break; + case 4000: val = 4; break; + case 6000: val = 5; break; + case 8000: val = 6; break; + case 12000: val = 7; break; + case 16000: val = 8; break; + default: val = 9; break; + } } else if (name == "Frequency Scale") { - *min = 0; - *max = 1; + *min = 0; + *max = 1; *deflt = int(BinScale::Linear); - val = (int)m_binScale; + val = (int)m_binScale; } else if (name == "Bin Display") { - *min = 0; - *max = 2; + *min = 0; + *max = 2; *deflt = int(BinDisplay::AllBins); - val = (int)m_binDisplay; + val = (int)m_binDisplay; } else if (name == "Normalization") { - + *min = 0; *max = 3; *deflt = 0; @@ -415,7 +437,7 @@ val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea); } else { - val = Layer::getPropertyRangeAndValue(name, min, max, deflt); + val = Layer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -423,82 +445,89 @@ QString SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Colour") { return ColourMapper::getColourMapName(value); } if (name == "Colour Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Meter"); - case 2: return tr("dBV^2"); - case 3: return tr("dBV"); - case 4: return tr("Phase"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Meter"); + case 2: return tr("dBV^2"); + case 3: return tr("dBV"); + case 4: return tr("Phase"); + } } if (name == "Normalization") { - return ""; // icon only + switch(value) { + default: + case 0: return tr("None"); + case 1: return tr("Col"); + case 2: return tr("View"); + case 3: return tr("Hybrid"); + } +// return ""; // icon only } if (name == "Window Size") { - return QString("%1").arg(32 << value); + return QString("%1").arg(32 << value); } if (name == "Window Increment") { - switch (value) { - default: - case 0: return tr("None"); - case 1: return tr("25 %"); - case 2: return tr("50 %"); - case 3: return tr("75 %"); - case 4: return tr("87.5 %"); - case 5: return tr("93.75 %"); - } + switch (value) { + default: + case 0: return tr("None"); + case 1: return tr("25 %"); + case 2: return tr("50 %"); + case 3: return tr("75 %"); + case 4: return tr("87.5 %"); + case 5: return tr("93.75 %"); + } } if (name == "Min Frequency") { - switch (value) { - default: - case 0: return tr("No min"); - case 1: return tr("10 Hz"); - case 2: return tr("20 Hz"); - case 3: return tr("40 Hz"); - case 4: return tr("100 Hz"); - case 5: return tr("250 Hz"); - case 6: return tr("500 Hz"); - case 7: return tr("1 KHz"); - case 8: return tr("4 KHz"); - case 9: return tr("10 KHz"); - } + switch (value) { + default: + case 0: return tr("No min"); + case 1: return tr("10 Hz"); + case 2: return tr("20 Hz"); + case 3: return tr("40 Hz"); + case 4: return tr("100 Hz"); + case 5: return tr("250 Hz"); + case 6: return tr("500 Hz"); + case 7: return tr("1 KHz"); + case 8: return tr("4 KHz"); + case 9: return tr("10 KHz"); + } } if (name == "Max Frequency") { - switch (value) { - default: - case 0: return tr("500 Hz"); - case 1: return tr("1 KHz"); - case 2: return tr("1.5 KHz"); - case 3: return tr("2 KHz"); - case 4: return tr("4 KHz"); - case 5: return tr("6 KHz"); - case 6: return tr("8 KHz"); - case 7: return tr("12 KHz"); - case 8: return tr("16 KHz"); - case 9: return tr("No max"); - } + switch (value) { + default: + case 0: return tr("500 Hz"); + case 1: return tr("1 KHz"); + case 2: return tr("1.5 KHz"); + case 3: return tr("2 KHz"); + case 4: return tr("4 KHz"); + case 5: return tr("6 KHz"); + case 6: return tr("8 KHz"); + case 7: return tr("12 KHz"); + case 8: return tr("16 KHz"); + case 9: return tr("No max"); + } } if (name == "Frequency Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Log"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + } } if (name == "Bin Display") { - switch (value) { - default: - case 0: return tr("All Bins"); - case 1: return tr("Peak Bins"); - case 2: return tr("Frequencies"); - } + switch (value) { + default: + case 0: return tr("All Bins"); + case 1: return tr("Peak Bins"); + case 2: return tr("Frequencies"); + } } return tr(""); } @@ -536,51 +565,51 @@ SpectrogramLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(float(pow(10, float(value)/20.0))); + setGain(float(pow(10, float(value)/20.0))); } else if (name == "Threshold") { - if (value == -81) setThreshold(0.0); - else setThreshold(float(AudioLevel::dB_to_multiplier(value))); + if (value == -81) setThreshold(0.0); + else setThreshold(float(AudioLevel::dB_to_multiplier(value))); } else if (name == "Colour Rotation") { - setColourRotation(value); + setColourRotation(value); } else if (name == "Colour") { setColourMap(value); } else if (name == "Window Size") { - setWindowSize(32 << value); + setWindowSize(32 << value); } else if (name == "Window Increment") { setWindowHopLevel(value); } else if (name == "Min Frequency") { - switch (value) { - default: - case 0: setMinFrequency(0); break; - case 1: setMinFrequency(10); break; - case 2: setMinFrequency(20); break; - case 3: setMinFrequency(40); break; - case 4: setMinFrequency(100); break; - case 5: setMinFrequency(250); break; - case 6: setMinFrequency(500); break; - case 7: setMinFrequency(1000); break; - case 8: setMinFrequency(4000); break; - case 9: setMinFrequency(10000); break; - } + switch (value) { + default: + case 0: setMinFrequency(0); break; + case 1: setMinFrequency(10); break; + case 2: setMinFrequency(20); break; + case 3: setMinFrequency(40); break; + case 4: setMinFrequency(100); break; + case 5: setMinFrequency(250); break; + case 6: setMinFrequency(500); break; + case 7: setMinFrequency(1000); break; + case 8: setMinFrequency(4000); break; + case 9: setMinFrequency(10000); break; + } int vs = getCurrentVerticalZoomStep(); if (vs != m_lastEmittedZoomStep) { emit verticalZoomChanged(); m_lastEmittedZoomStep = vs; } } else if (name == "Max Frequency") { - switch (value) { - case 0: setMaxFrequency(500); break; - case 1: setMaxFrequency(1000); break; - case 2: setMaxFrequency(1500); break; - case 3: setMaxFrequency(2000); break; - case 4: setMaxFrequency(4000); break; - case 5: setMaxFrequency(6000); break; - case 6: setMaxFrequency(8000); break; - case 7: setMaxFrequency(12000); break; - case 8: setMaxFrequency(16000); break; - default: - case 9: setMaxFrequency(0); break; - } + switch (value) { + case 0: setMaxFrequency(500); break; + case 1: setMaxFrequency(1000); break; + case 2: setMaxFrequency(1500); break; + case 3: setMaxFrequency(2000); break; + case 4: setMaxFrequency(4000); break; + case 5: setMaxFrequency(6000); break; + case 6: setMaxFrequency(8000); break; + case 7: setMaxFrequency(12000); break; + case 8: setMaxFrequency(16000); break; + default: + case 9: setMaxFrequency(0); break; + } int vs = getCurrentVerticalZoomStep(); if (vs != m_lastEmittedZoomStep) { emit verticalZoomChanged(); @@ -588,30 +617,30 @@ } } else if (name == "Colour Scale") { setColourScaleMultiple(1.0); - switch (value) { - default: - case 0: setColourScale(ColourScaleType::Linear); break; - case 1: setColourScale(ColourScaleType::Meter); break; - case 2: + switch (value) { + default: + case 0: setColourScale(ColourScaleType::Linear); break; + case 1: setColourScale(ColourScaleType::Meter); break; + case 2: setColourScale(ColourScaleType::Log); setColourScaleMultiple(2.0); break; - case 3: setColourScale(ColourScaleType::Log); break; - case 4: setColourScale(ColourScaleType::Phase); break; - } + case 3: setColourScale(ColourScaleType::Log); break; + case 4: setColourScale(ColourScaleType::Phase); break; + } } else if (name == "Frequency Scale") { - switch (value) { - default: - case 0: setBinScale(BinScale::Linear); break; - case 1: setBinScale(BinScale::Log); break; - } + switch (value) { + default: + case 0: setBinScale(BinScale::Linear); break; + case 1: setBinScale(BinScale::Log); break; + } } else if (name == "Bin Display") { - switch (value) { - default: - case 0: setBinDisplay(BinDisplay::AllBins); break; - case 1: setBinDisplay(BinDisplay::PeakBins); break; - case 2: setBinDisplay(BinDisplay::PeakFrequencies); break; - } + switch (value) { + default: + case 0: setBinDisplay(BinDisplay::AllBins); break; + case 1: setBinDisplay(BinDisplay::PeakBins); break; + case 2: setBinDisplay(BinDisplay::PeakFrequencies); break; + } } else if (name == "Normalization") { auto n = convertToColumnNorm(value); setNormalization(n.first); @@ -665,7 +694,7 @@ invalidateRenderers(); m_channel = ch; - invalidateFFTModel(); + recreateFFTModel(); emit layerParametersChanged(); } @@ -709,7 +738,7 @@ m_windowSize = ws; - invalidateFFTModel(); + recreateFFTModel(); emit layerParametersChanged(); } @@ -729,11 +758,9 @@ m_windowHopLevel = v; - invalidateFFTModel(); + recreateFFTModel(); emit layerParametersChanged(); - -// fillCache(); } int @@ -751,7 +778,7 @@ m_windowType = w; - invalidateFFTModel(); + recreateFFTModel(); emit layerParametersChanged(); } @@ -766,7 +793,7 @@ SpectrogramLayer::setGain(float gain) { // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " -// << m_gain << ")" << endl; +// << m_gain << ")" << endl; if (m_gain == gain) return; @@ -851,7 +878,7 @@ int distance = r - m_colourRotation; if (distance != 0) { - m_colourRotation = r; + m_colourRotation = r; } // Initially the idea with colour rotation was that we would just @@ -1003,14 +1030,20 @@ Layer::setLayerDormant(v, true); - invalidateRenderers(); - + invalidateRenderers(); + } else { Layer::setLayerDormant(v, false); } } +bool +SpectrogramLayer::isLayerScrollable(const LayerGeometryProvider *) const +{ + return false; +} + void SpectrogramLayer::cacheInvalid() { @@ -1058,9 +1091,9 @@ double minf = double(sr) / getFFTSize(); if (m_minFrequency > 0.0) { - int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01); - if (minbin < 1) minbin = 1; - minf = minbin * sr / getFFTSize(); + int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01); + if (minbin < 1) minbin = 1; + minf = minbin * sr / getFFTSize(); } return minf; @@ -1073,9 +1106,9 @@ double maxf = double(sr) / 2; if (m_maxFrequency > 0.0) { - int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); - if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2; - maxf = maxbin * sr / getFFTSize(); + int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); + if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2; + maxf = maxbin * sr / getFFTSize(); } return maxf; @@ -1135,7 +1168,7 @@ sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1; if (f1 < int(modelStart) || f0 > int(modelEnd)) { - return false; + return false; } // And that range may be drawn from a possibly non-integral @@ -1160,7 +1193,7 @@ int windowIncrement = getWindowIncrement(); int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; int w1 = s1i * windowIncrement + windowIncrement + - (m_windowSize - windowIncrement)/2 - 1; + (m_windowSize - windowIncrement)/2 - 1; min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); @@ -1180,20 +1213,20 @@ sv_samplerate_t sr = m_model->getSampleRate(); for (int q = q0i; q <= q1i; ++q) { - if (q == q0i) freqMin = (sr * q) / getFFTSize(); - if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize(); + if (q == q0i) freqMin = (sr * q) / getFFTSize(); + if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize(); } return true; } bool SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, - double &freqMin, double &freqMax, - double &adjFreqMin, double &adjFreqMax) + double &freqMin, double &freqMax, + double &adjFreqMin, double &adjFreqMax) const { if (!m_model || !m_model->isOK() || !m_model->isReady()) { - return false; + return false; } FFTModel *fft = getFFTModel(); @@ -1216,39 +1249,39 @@ bool haveAdj = false; bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins || - m_binDisplay == BinDisplay::PeakFrequencies); + m_binDisplay == BinDisplay::PeakFrequencies); for (int q = q0i; q <= q1i; ++q) { - for (int s = s0i; s <= s1i; ++s) { - - double binfreq = (double(sr) * q) / m_windowSize; - if (q == q0i) freqMin = binfreq; - if (q == q1i) freqMax = binfreq; - - if (peaksOnly && !fft->isLocalPeak(s, q)) continue; - - if (!fft->isOverThreshold + for (int s = s0i; s <= s1i; ++s) { + + double binfreq = (double(sr) * q) / m_windowSize; + if (q == q0i) freqMin = binfreq; + if (q == q1i) freqMax = binfreq; + + if (peaksOnly && !fft->isLocalPeak(s, q)) continue; + + if (!fft->isOverThreshold (s, q, float(m_threshold * double(getFFTSize())/2.0))) { continue; } double freq = binfreq; - - if (s < int(fft->getWidth()) - 1) { + + if (s < int(fft->getWidth()) - 1) { fft->estimateStableFrequency(s, q, freq); - - if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; - if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; - - haveAdj = true; - } - } + + if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; + if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; + + haveAdj = true; + } + } } if (!haveAdj) { - adjFreqMin = adjFreqMax = 0.0; + adjFreqMin = adjFreqMax = 0.0; } return haveAdj; @@ -1256,11 +1289,11 @@ bool SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, - double &min, double &max, - double &phaseMin, double &phaseMax) const + double &min, double &max, + double &phaseMin, double &phaseMax) const { if (!m_model || !m_model->isOK() || !m_model->isReady()) { - return false; + return false; } double q0 = 0, q1 = 0; @@ -1305,7 +1338,7 @@ if (!have || value > max) { max = value; } have = true; - } + } } } @@ -1316,61 +1349,92 @@ return rv; } - -FFTModel * -SpectrogramLayer::getFFTModel() const + +void +SpectrogramLayer::recreateFFTModel() { - if (!m_model) return 0; - - int fftSize = getFFTSize(); - - //!!! it is now surely slower to do this on every getFFTModel() - //!!! request than it would be to recreate the model immediately - //!!! when something changes instead of just invalidating it + SVDEBUG << "SpectrogramLayer::recreateFFTModel called" << endl; + + if (!m_model || !m_model->isOK()) { + emit sliceableModelReplaced(m_fftModel, 0); + deleteDerivedModels(); + return; + } + + if (m_fftModel) m_fftModel->aboutToDelete(); - if (m_fftModel && - m_fftModel->getHeight() == fftSize / 2 + 1 && - m_fftModel->getWindowIncrement() == getWindowIncrement()) { - return m_fftModel; - } - + if (m_peakCache) m_peakCache->aboutToDelete(); delete m_peakCache; m_peakCache = 0; - delete m_fftModel; - m_fftModel = new FFTModel(m_model, - m_channel, - m_windowType, - m_windowSize, - getWindowIncrement(), - fftSize); - - if (!m_fftModel->isOK()) { + if (m_wholeCache) m_wholeCache->aboutToDelete(); + delete m_wholeCache; + m_wholeCache = 0; + + FFTModel *newModel = new FFTModel(m_model, + m_channel, + m_windowType, + m_windowSize, + getWindowIncrement(), + getFFTSize()); + + if (!newModel->isOK()) { QMessageBox::critical (0, tr("FFT cache failed"), tr("Failed to create the FFT model for this spectrogram.\n" "There may be insufficient memory or disc space to continue.")); + delete newModel; delete m_fftModel; m_fftModel = 0; - return 0; + return; } - ((SpectrogramLayer *)this)->sliceableModelReplaced(0, m_fftModel); - - return m_fftModel; + FFTModel *oldModel = m_fftModel; + m_fftModel = newModel; + + if (canStoreWholeCache()) { // i.e. if enough memory + m_wholeCache = new Dense3DModelPeakCache(m_fftModel, 1); + m_peakCache = new Dense3DModelPeakCache(m_wholeCache, m_peakCacheDivisor); + } else { + m_peakCache = new Dense3DModelPeakCache(m_fftModel, m_peakCacheDivisor); + } + + emit sliceableModelReplaced(oldModel, m_fftModel); + delete oldModel; } -Dense3DModelPeakCache * -SpectrogramLayer::getPeakCache() const +bool +SpectrogramLayer::canStoreWholeCache() const { - //!!! see comment in getFFTModel - - if (!m_peakCache) { - FFTModel *f = getFFTModel(); - if (!f) return 0; - m_peakCache = new Dense3DModelPeakCache(f, m_peakCacheDivisor); + if (!m_fftModel) { + return false; // or true, doesn't really matter } - return m_peakCache; + + size_t sz = + size_t(m_fftModel->getWidth()) * + size_t(m_fftModel->getHeight()) * + sizeof(float); + + try { + SVDEBUG << "Requesting advice from StorageAdviser on whether to create whole-model cache" << endl; + StorageAdviser::Recommendation recommendation = + StorageAdviser::recommend + (StorageAdviser::Criteria(StorageAdviser::SpeedCritical | + StorageAdviser::PrecisionCritical | + StorageAdviser::FrequentLookupLikely), + sz / 1024, sz / 1024); + if ((recommendation & StorageAdviser::UseDisc) || + (recommendation & StorageAdviser::ConserveSpace)) { + SVDEBUG << "Seems inadvisable to create whole-model cache" << endl; + return false; + } else { + SVDEBUG << "Seems fine to create whole-model cache" << endl; + return true; + } + } catch (const InsufficientDiscSpace &) { + SVDEBUG << "Seems like a terrible idea to create whole-model cache" << endl; + return false; + } } const Model * @@ -1380,22 +1444,6 @@ } void -SpectrogramLayer::invalidateFFTModel() -{ -#ifdef DEBUG_SPECTROGRAM - cerr << "SpectrogramLayer::invalidateFFTModel called" << endl; -#endif - - emit sliceableModelReplaced(m_fftModel, 0); - - delete m_fftModel; - delete m_peakCache; - - m_fftModel = 0; - m_peakCache = 0; -} - -void SpectrogramLayer::invalidateMagnitudes() { #ifdef DEBUG_SPECTROGRAM @@ -1421,7 +1469,8 @@ sources.verticalBinLayer = this; sources.fft = getFFTModel(); sources.source = sources.fft; - sources.peakCache = getPeakCache(); + if (m_peakCache) sources.peakCaches.push_back(m_peakCache); + if (m_wholeCache) sources.peakCaches.push_back(m_wholeCache); ColourScale::Parameters cparams; cparams.colourMap = m_colourMap; @@ -1433,8 +1482,8 @@ cparams.threshold = m_threshold; } - float minValue = 0.0f; - float maxValue = 1.0f; + double minValue = 0.0f; + double maxValue = 1.0f; if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) { minValue = m_viewMags[viewId].getMin(); @@ -1454,7 +1503,8 @@ cparams.minValue = minValue; cparams.maxValue = maxValue; - m_lastRenderedMags[viewId] = MagnitudeRange(minValue, maxValue); + m_lastRenderedMags[viewId] = MagnitudeRange(float(minValue), + float(maxValue)); Colour3DPlotRenderer::Parameters params; params.colourScale = ColourScale(cparams); @@ -1477,10 +1527,13 @@ (smoothing == Preferences::SpectrogramInterpolated || smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated); - m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params); + m_renderers[viewId] = new Colour3DPlotRenderer(sources, params); + + m_crosshairColour = + ColourMapper(m_colourMap, 1.f, 255.f).getContrastingColour(); } - return m_renderers[v->getId()]; + return m_renderers[viewId]; } void @@ -1554,11 +1607,7 @@ #endif if (!m_model || !m_model->isOK() || !m_model->isReady()) { - return; - } - - if (isLayerDormant(v)) { - SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; + return; } paintWithRenderer(v, paint, rect); @@ -1613,18 +1662,18 @@ SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const { return v->getYForFrequency(frequency, - getEffectiveMinFrequency(), - getEffectiveMaxFrequency(), - m_binScale == BinScale::Log); + getEffectiveMinFrequency(), + getEffectiveMaxFrequency(), + m_binScale == BinScale::Log); } double SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const { return v->getFrequencyForY(y, - getEffectiveMinFrequency(), - getEffectiveMaxFrequency(), - m_binScale == BinScale::Log); + getEffectiveMinFrequency(), + getEffectiveMaxFrequency(), + m_binScale == BinScale::Log); } int @@ -1714,8 +1763,8 @@ bool SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, sv_frame_t &frame, - int &resolution, - SnapType snap) const + int &resolution, + SnapType snap) const { resolution = getWindowIncrement(); sv_frame_t left = (frame / resolution) * resolution; @@ -1726,9 +1775,9 @@ case SnapRight: frame = right; break; case SnapNearest: case SnapNeighbouring: - if (frame - left > right - frame) frame = right; - else frame = left; - break; + if (frame - left > right - frame) frame = right; + else frame = left; + break; } return true; @@ -1884,95 +1933,95 @@ bool haveValues = false; if (!getXBinSourceRange(v, x, rtMin, rtMax)) { - return ""; + return ""; } if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { - haveValues = true; + haveValues = true; } QString adjFreqText = "", adjPitchText = ""; if (m_binDisplay == BinDisplay::PeakFrequencies) { - if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, - adjFreqMin, adjFreqMax)) { - return ""; - } - - if (adjFreqMin != adjFreqMax) { - adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") - .arg(adjFreqMin).arg(adjFreqMax); - } else { - adjFreqText = tr("Peak Frequency:\t%1 Hz\n") - .arg(adjFreqMin); - } - - QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); - QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); - - if (pmin != pmax) { - adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); - } else { - adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); - } + if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, + adjFreqMin, adjFreqMax)) { + return ""; + } + + if (adjFreqMin != adjFreqMax) { + adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") + .arg(adjFreqMin).arg(adjFreqMax); + } else { + adjFreqText = tr("Peak Frequency:\t%1 Hz\n") + .arg(adjFreqMin); + } + + QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); + QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); + + if (pmin != pmax) { + adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); + } else { + adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); + } } else { - - if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; + + if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; } QString text; if (rtMin != rtMax) { - text += tr("Time:\t%1 - %2\n") - .arg(rtMin.toText(true).c_str()) - .arg(rtMax.toText(true).c_str()); + text += tr("Time:\t%1 - %2\n") + .arg(rtMin.toText(true).c_str()) + .arg(rtMax.toText(true).c_str()); } else { - text += tr("Time:\t%1\n") - .arg(rtMin.toText(true).c_str()); + text += tr("Time:\t%1\n") + .arg(rtMin.toText(true).c_str()); } if (freqMin != freqMax) { - text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") - .arg(adjFreqText) - .arg(freqMin) - .arg(freqMax) - .arg(adjPitchText) - .arg(Pitch::getPitchLabelForFrequency(freqMin)) - .arg(Pitch::getPitchLabelForFrequency(freqMax)); + text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") + .arg(adjFreqText) + .arg(freqMin) + .arg(freqMax) + .arg(adjPitchText) + .arg(Pitch::getPitchLabelForFrequency(freqMin)) + .arg(Pitch::getPitchLabelForFrequency(freqMax)); } else { - text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") - .arg(adjFreqText) - .arg(freqMin) - .arg(adjPitchText) - .arg(Pitch::getPitchLabelForFrequency(freqMin)); - } + text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") + .arg(adjFreqText) + .arg(freqMin) + .arg(adjPitchText) + .arg(Pitch::getPitchLabelForFrequency(freqMin)); + } if (haveValues) { - double dbMin = AudioLevel::multiplier_to_dB(magMin); - double dbMax = AudioLevel::multiplier_to_dB(magMax); - QString dbMinString; - QString dbMaxString; - if (dbMin == AudioLevel::DB_FLOOR) { - dbMinString = Strings::minus_infinity; - } else { - dbMinString = QString("%1").arg(lrint(dbMin)); - } - if (dbMax == AudioLevel::DB_FLOOR) { - dbMaxString = Strings::minus_infinity; - } else { - dbMaxString = QString("%1").arg(lrint(dbMax)); - } - if (lrint(dbMin) != lrint(dbMax)) { - text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); - } else { - text += tr("dB:\t%1").arg(dbMinString); - } - if (phaseMin != phaseMax) { - text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); - } else { - text += tr("\nPhase:\t%1").arg(phaseMin); - } + double dbMin = AudioLevel::multiplier_to_dB(magMin); + double dbMax = AudioLevel::multiplier_to_dB(magMax); + QString dbMinString; + QString dbMaxString; + if (dbMin == AudioLevel::DB_FLOOR) { + dbMinString = Strings::minus_infinity; + } else { + dbMinString = QString("%1").arg(lrint(dbMin)); + } + if (dbMax == AudioLevel::DB_FLOOR) { + dbMaxString = Strings::minus_infinity; + } else { + dbMaxString = QString("%1").arg(lrint(dbMax)); + } + if (lrint(dbMin) != lrint(dbMax)) { + text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); + } else { + text += tr("dB:\t%1").arg(dbMinString); + } + if (phaseMin != phaseMax) { + text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); + } else { + text += tr("\nPhase:\t%1").arg(phaseMin); + } } return text; @@ -1997,9 +2046,9 @@ if (detailed) cw = getColourScaleWidth(paint); int tw = paint.fontMetrics().width(QString("%1") - .arg(m_maxFrequency > 0 ? - m_maxFrequency - 1 : - m_model->getSampleRate() / 2)); + .arg(m_maxFrequency > 0 ? + m_maxFrequency - 1 : + m_model->getSampleRate() / 2)); int fw = paint.fontMetrics().width(tr("43Hz")); if (tw < fw) tw = fw; @@ -2014,7 +2063,7 @@ QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) { - return; + return; } Profiler profiler("SpectrogramLayer::paintVerticalScale"); @@ -2036,8 +2085,8 @@ sv_samplerate_t sr = m_model->getSampleRate(); if (m_maxFrequency > 0) { - bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); - if (bins > getFFTSize() / 2) bins = getFFTSize() / 2; + bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); + if (bins > getFFTSize() / 2) bins = getFFTSize() / 2; } int cw = 0; @@ -2052,37 +2101,37 @@ for (int y = 0; y < v->getPaintHeight(); ++y) { - double q0, q1; - if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; - - int vy; - - if (int(q0) > bin) { - vy = y; - bin = int(q0); - } else { - continue; - } - - int freq = int((sr * bin) / getFFTSize()); - - if (py >= 0 && (vy - py) < textHeight - 1) { - if (m_binScale == BinScale::Linear) { - paint.drawLine(w - tickw, h - vy, w, h - vy); - } - continue; - } - - QString text = QString("%1").arg(freq); - if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC - paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); - - if (h - vy - textHeight >= -2) { - int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); - paint.drawText(tx, h - vy + toff, text); - } - - py = vy; + double q0, q1; + if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; + + int vy; + + if (int(q0) > bin) { + vy = y; + bin = int(q0); + } else { + continue; + } + + int freq = int((sr * bin) / getFFTSize()); + + if (py >= 0 && (vy - py) < textHeight - 1) { + if (m_binScale == BinScale::Linear) { + paint.drawLine(w - tickw, h - vy, w, h - vy); + } + continue; + } + + QString text = QString("%1").arg(freq); + if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC + paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); + + if (h - vy - textHeight >= -2) { + int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); + paint.drawText(tx, h - vy + toff, text); + } + + py = vy; } if (m_binScale == BinScale::Log) { @@ -2118,7 +2167,7 @@ int topLines = 2; int ch = h - textHeight * (topLines + 1) - 8; -// paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); +// paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); QString top, bottom; @@ -2446,30 +2495,30 @@ QString s; s += QString("channel=\"%1\" " - "windowSize=\"%2\" " - "windowHopLevel=\"%3\" " - "gain=\"%4\" " - "threshold=\"%5\" ") - .arg(m_channel) - .arg(m_windowSize) - .arg(m_windowHopLevel) - .arg(m_gain) - .arg(m_threshold); + "windowSize=\"%2\" " + "windowHopLevel=\"%3\" " + "gain=\"%4\" " + "threshold=\"%5\" ") + .arg(m_channel) + .arg(m_windowSize) + .arg(m_windowHopLevel) + .arg(m_gain) + .arg(m_threshold); s += QString("minFrequency=\"%1\" " - "maxFrequency=\"%2\" " - "colourScale=\"%3\" " - "colourScheme=\"%4\" " - "colourRotation=\"%5\" " - "frequencyScale=\"%6\" " - "binDisplay=\"%7\" ") - .arg(m_minFrequency) - .arg(m_maxFrequency) - .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple)) - .arg(m_colourMap) - .arg(m_colourRotation) - .arg(int(m_binScale)) - .arg(int(m_binDisplay)); + "maxFrequency=\"%2\" " + "colourScale=\"%3\" " + "colourScheme=\"%4\" " + "colourRotation=\"%5\" " + "frequencyScale=\"%6\" " + "binDisplay=\"%7\" ") + .arg(m_minFrequency) + .arg(m_maxFrequency) + .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple)) + .arg(m_colourMap) + .arg(m_colourRotation) + .arg(int(m_binScale)) + .arg(int(m_binDisplay)); // New-style normalization attributes, allowing for more types of // normalization in future: write out the column normalization @@ -2487,7 +2536,7 @@ // v2.0+ will look odd in Tony v1.0 s += QString("normalizeColumns=\"%1\" ") - .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false"); + .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false"); // And this applies to both old- and new-style attributes @@ -2554,11 +2603,11 @@ if (ok) setColourRotation(colourRotation); BinScale binScale = (BinScale) - attributes.value("frequencyScale").toInt(&ok); + attributes.value("frequencyScale").toInt(&ok); if (ok) setBinScale(binScale); BinDisplay binDisplay = (BinDisplay) - attributes.value("binDisplay").toInt(&ok); + attributes.value("binDisplay").toInt(&ok); if (ok) setBinDisplay(binDisplay); bool haveNewStyleNormalization = false; @@ -2576,7 +2625,7 @@ } else if (columnNormalization == "none") { setNormalization(ColumnNormalization::None); } else { - cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" + SVCERR << "NOTE: Unknown or unsupported columnNormalization attribute \"" << columnNormalization << "\"" << endl; } } diff -r 57d192e26331 -r 13d9b422f7fe layer/SpectrogramLayer.h --- a/layer/SpectrogramLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SpectrogramLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -49,7 +49,7 @@ */ class SpectrogramLayer : public VerticalBinLayer, - public PowerOfSqrtTwoZoomConstraint + public PowerOfSqrtTwoZoomConstraint { Q_OBJECT @@ -74,8 +74,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *); @@ -91,7 +91,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual QString getPropertyValueIconName(const PropertyName &, int value) const; virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; @@ -189,7 +189,7 @@ int getColourRotation() const; virtual VerticalPosition getPreferredFrameCountPosition() const { - return PositionTop; + return PositionTop; } virtual bool isLayerOpaque() const { return true; } @@ -224,7 +224,7 @@ virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); - virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const; virtual int getVerticalZoomSteps(int &defaultStep) const; virtual int getCurrentVerticalZoomStep() const; @@ -258,7 +258,7 @@ ColourScaleType m_colourScale; double m_colourScaleMultiple; int m_colourMap; - QColor m_crosshairColour; + mutable QColor m_crosshairColour; BinScale m_binScale; BinDisplay m_binDisplay; ColumnNormalization m_normalization; // of individual columns @@ -287,11 +287,11 @@ bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const; bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, - double &freqMin, double &freqMax, - double &adjFreqMin, double &adjFreqMax) const; + double &freqMin, double &freqMax, + double &adjFreqMin, double &adjFreqMax) const; bool getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &timeMin, RealTime &timeMax) const; bool getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &min, double &max, - double &phaseMin, double &phaseMax) const; + double &phaseMin, double &phaseMax) const; int getWindowIncrement() const { if (m_windowHopLevel == 0) return m_windowSize; @@ -302,9 +302,14 @@ int getFFTOversampling() const; int getFFTSize() const; // m_windowSize * getFFTOversampling() - mutable FFTModel *m_fftModel; //!!! should not be mutable, see getFFTModel()? - mutable Dense3DModelPeakCache *m_peakCache; + FFTModel *m_fftModel; + FFTModel *getFFTModel() const { return m_fftModel; } + Dense3DModelPeakCache *m_wholeCache; + Dense3DModelPeakCache *m_peakCache; + Dense3DModelPeakCache *getPeakCache() const { return m_peakCache; } const int m_peakCacheDivisor; + bool canStoreWholeCache() const; + void recreateFFTModel(); typedef std::map ViewMagMap; // key is view id mutable ViewMagMap m_viewMags; @@ -315,11 +320,9 @@ mutable ViewRendererMap m_renderers; Colour3DPlotRenderer *getRenderer(LayerGeometryProvider *) const; void invalidateRenderers(); + + void deleteDerivedModels(); - FFTModel *getFFTModel() const; - Dense3DModelPeakCache *getPeakCache() const; - void invalidateFFTModel(); - void paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; void paintDetailedScale(LayerGeometryProvider *v, diff -r 57d192e26331 -r 13d9b422f7fe layer/SpectrumLayer.cpp --- a/layer/SpectrumLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SpectrumLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -25,6 +25,8 @@ #include "ColourMapper.h" #include "PaintAssistant.h" +#include "PianoScale.h" +#include "HorizontalFrequencyScale.h" #include #include @@ -166,7 +168,7 @@ SpectrumLayer::getPropertyGroupName(const PropertyName &name) const { if (name == "Window Size" || - name == "Window Increment") return tr("Window"); + name == "Window Increment") return tr("Window"); if (name == "Show Peak Frequencies") return tr("Bins"); return SliceLayer::getPropertyGroupName(name); } @@ -184,20 +186,20 @@ if (name == "Window Size") { - *min = 0; - *max = 15; + *min = 0; + *max = 15; *deflt = 5; - - val = 0; - int ws = m_windowSize; - while (ws > 32) { ws >>= 1; val ++; } + + val = 0; + int ws = m_windowSize; + while (ws > 32) { ws >>= 1; val ++; } } else if (name == "Window Increment") { - - *min = 0; - *max = 5; + + *min = 0; + *max = 5; *deflt = 2; - + val = m_windowHopLevel; } else if (name == "Show Peak Frequencies") { @@ -214,21 +216,21 @@ QString SpectrumLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Window Size") { - return QString("%1").arg(32 << value); + return QString("%1").arg(32 << value); } if (name == "Window Increment") { - switch (value) { - default: - case 0: return tr("None"); - case 1: return tr("25 %"); - case 2: return tr("50 %"); - case 3: return tr("75 %"); - case 4: return tr("87.5 %"); - case 5: return tr("93.75 %"); - } + switch (value) { + default: + case 0: return tr("None"); + case 1: return tr("25 %"); + case 2: return tr("50 %"); + case 3: return tr("75 %"); + case 4: return tr("87.5 %"); + case 5: return tr("93.75 %"); + } } return SliceLayer::getPropertyValueLabel(name, value); } @@ -243,7 +245,7 @@ SpectrumLayer::setProperty(const PropertyName &name, int value) { if (name == "Window Size") { - setWindowSize(32 << value); + setWindowSize(32 << value); } else if (name == "Window Increment") { setWindowHopLevel(value); } else if (name == "Show Peak Frequencies") { @@ -297,98 +299,34 @@ } } -bool -SpectrumLayer::getValueExtents(double &, double &, bool &, QString &) const +double +SpectrumLayer::getFrequencyForX(const LayerGeometryProvider *v, double x) const { - return false; + if (!m_sliceableModel) return 0; + double bin = getBinForX(v, x); + // we assume the frequency of a bin corresponds to the centre of + // its visual range + bin -= 0.5; + return (m_sliceableModel->getSampleRate() * bin) / + (m_sliceableModel->getHeight() * 2); } double -SpectrumLayer::getXForBin(int bin, int totalBins, double w) const +SpectrumLayer::getXForFrequency(const LayerGeometryProvider *v, double freq) const { - if (!m_sliceableModel) return SliceLayer::getXForBin(bin, totalBins, w); - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - double binfreq = (sampleRate * bin) / (totalBins * 2); - - return getXForFrequency(binfreq, w); -} - -int -SpectrumLayer::getBinForX(double x, int totalBins, double w) const -{ - if (!m_sliceableModel) return SliceLayer::getBinForX(x, totalBins, w); - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - double binfreq = getFrequencyForX(x, w); - - return int((binfreq * totalBins * 2) / sampleRate); -} - -double -SpectrumLayer::getFrequencyForX(double x, double w) const -{ - double freq = 0; if (!m_sliceableModel) return 0; - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - - double maxfreq = double(sampleRate) / 2; - - switch (m_binScale) { - - case LinearBins: - freq = ((x * maxfreq) / w); - break; - - case LogBins: - freq = pow(10.0, (x * log10(maxfreq)) / w); - break; - - case InvertedLogBins: - freq = maxfreq - pow(10.0, ((w - x) * log10(maxfreq)) / w); - break; - } - - return freq; -} - -double -SpectrumLayer::getXForFrequency(double freq, double w) const -{ - double x = 0; - if (!m_sliceableModel) return x; - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - - double maxfreq = double(sampleRate) / 2; - - switch (m_binScale) { - - case LinearBins: - x = (freq * w) / maxfreq; - break; - - case LogBins: - x = (log10(freq) * w) / log10(maxfreq); - break; - - case InvertedLogBins: - if (maxfreq == freq) x = w; - else x = w - (log10(maxfreq - freq) * w) / log10(maxfreq); - break; - } - - return x; + double bin = (freq * m_sliceableModel->getHeight() * 2) / + m_sliceableModel->getSampleRate(); + // we want the centre of the bin range + bin += 0.5; + return getXForBin(v, bin); } bool SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, double &value, QString &unit) const { - if (m_xorigins.find(v) == m_xorigins.end()) return false; - int xorigin = m_xorigins.find(v)->second; - value = getFrequencyForX(x - xorigin, v->getPaintWidth() - xorigin - 1); + value = getFrequencyForX(v, x); unit = "Hz"; return true; } @@ -397,7 +335,7 @@ SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y, double &value, QString &unit) const { - value = getValueForY(y, v); + value = getValueForY(v, y); if (m_energyScale == dBScale || m_energyScale == MeterScale) { @@ -483,33 +421,32 @@ ColourMapper mapper(m_colourMap, 0, 1); paint.setPen(mapper.getContrastingColour()); - int xorigin = m_xorigins[v]; - int w = v->getPaintWidth() - xorigin - 1; - + int xorigin = m_xorigins[v->getId()]; paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y()); paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight()); - double fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); + double fundamental = getFrequencyForX(v, cursorPos.x()); int hoffset = 2; if (m_binScale == LogBins) hoffset = 13; PaintAssistant::drawVisibleText(v, paint, - cursorPos.x() + 2, - v->getPaintHeight() - 2 - hoffset, - QString("%1 Hz").arg(fundamental), - PaintAssistant::OutlinedText); + cursorPos.x() + 2, + v->getPaintHeight() - 2 - hoffset, + QString("%1 Hz").arg(fundamental), + PaintAssistant::OutlinedText); if (Pitch::isFrequencyInMidiRange(fundamental)) { QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); PaintAssistant::drawVisibleText(v, paint, - cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2, - v->getPaintHeight() - 2 - hoffset, - pitchLabel, - PaintAssistant::OutlinedText); + cursorPos.x() - + paint.fontMetrics().width(pitchLabel) - 2, + v->getPaintHeight() - 2 - hoffset, + pitchLabel, + PaintAssistant::OutlinedText); } - double value = getValueForY(cursorPos.y(), v); + double value = getValueForY(v, cursorPos.y()); double thresh = m_threshold; double db = thresh; if (value > 0.0) db = 10.0 * log10(value); @@ -531,8 +468,7 @@ while (harmonic < 100) { - int hx = int(lrint(getXForFrequency(fundamental * harmonic, w))); - hx += xorigin; + int hx = int(lrint(getXForFrequency(v, fundamental * harmonic))); if (hx < xorigin || hx > v->getPaintWidth()) break; @@ -568,19 +504,22 @@ if (genericDesc == "") return ""; - double minvalue = 0.f; - if (minbin < int(m_values.size())) minvalue = m_values[minbin]; + int i0 = minbin - m_minbin; + int i1 = maxbin - m_minbin; + + float minvalue = 0.0; + if (in_range_for(m_values, i0)) minvalue = m_values[i0]; - double maxvalue = minvalue; - if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; - + float maxvalue = minvalue; + if (in_range_for(m_values, i1)) maxvalue = m_values[i1]; + if (minvalue > maxvalue) std::swap(minvalue, maxvalue); QString binstr; QString hzstr; int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) / m_windowSize)); - int maxfreq = int(lrint((std::max(maxbin, minbin+1) + int maxfreq = int(lrint((std::max(maxbin, minbin) * m_sliceableModel->getSampleRate()) / m_windowSize)); @@ -666,27 +605,22 @@ double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj int xorigin = getVerticalScaleWidth(v, false, paint) + 1; - int w = v->getPaintWidth() - xorigin - 1; - - int pkh = 0; -//!!! if (m_binScale == LogBins) { - pkh = 10; -//!!! } - - paint.save(); + int scaleHeight = getHorizontalScaleHeight(v, paint); if (fft && m_showPeaks) { // draw peak lines -// SVDEBUG << "Showing peaks..." << endl; - int col = int(v->getCentreFrame() / fft->getResolution()); paint.save(); paint.setRenderHint(QPainter::Antialiasing, false); - paint.setPen(QColor(160, 160, 160)); //!!! + ColourMapper mapper = + hasLightBackground() ? + ColourMapper(ColourMapper::BlackOnWhite, 0, 1) : + ColourMapper(ColourMapper::WhiteOnBlack, 0, 1); + int peakminbin = 0; int peakmaxbin = fft->getHeight() - 1; double peakmaxfreq = Pitch::getFrequencyForPitch(128); @@ -695,8 +629,6 @@ FFTModel::PeakSet peaks = fft->getPeakFrequencies (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin); - ColourMapper mapper(ColourMapper::BlackOnWhite, 0, 1); - BiasCurve curve; getBiasCurve(curve); int cs = int(curve.size()); @@ -720,106 +652,88 @@ double freq = i->second; - int x = int(lrint(getXForFrequency(freq, w))); + int x = int(lrint(getXForFrequency(v, freq))); double norm = 0.f; - (void)getYForValue(values[bin], v, norm); // don't need return value, need norm + (void)getYForValue(v, values[bin], norm); // don't need return value, need norm paint.setPen(mapper.map(norm)); - paint.drawLine(xorigin + x, 0, xorigin + x, v->getPaintHeight() - pkh - 1); + paint.drawLine(x, 0, x, v->getPaintHeight() - scaleHeight - 1); } paint.restore(); } + paint.save(); + SliceLayer::paint(v, paint, rect); - - //!!! All of this stuff relating to depicting frequencies - //(keyboard, crosshairs etc) should be applicable to any slice - //layer whose model has a vertical scale unit of Hz. However, the - //dense 3d model at the moment doesn't record its vertical scale - //unit -- we need to fix that and hoist this code as appropriate. - //Same really goes for any code in SpectrogramLayer that could be - //relevant to Colour3DPlotLayer with unit Hz, but that's a bigger - //proposition. - -// if (m_binScale == LogBins) { - -// int pkh = 10; - int h = v->getPaintHeight(); - - // piano keyboard - //!!! should be in a new paintHorizontalScale()? - // nice to have a piano keyboard class, of course - - paint.drawLine(xorigin, h - pkh - 1, w + xorigin, h - pkh - 1); - - int px = xorigin, ppx = xorigin; - paint.setBrush(paint.pen().color()); - - for (int i = 0; i < 128; ++i) { - - double f = Pitch::getFrequencyForPitch(i); - int x = int(lrint(getXForFrequency(f, w))); - - x += xorigin; - - if (i == 0) { - px = ppx = x; - } - if (i == 1) { - ppx = px - (x - px); - } - - if (x < xorigin) { - ppx = px; - px = x; - continue; - } - - if (x > w) { - break; - } - - int n = (i % 12); - - if (n == 1) { - // C# -- fill the C from here - QColor col = Qt::gray; - if (i == 61) { // filling middle C - col = Qt::blue; - col = col.light(150); - } - if (x - ppx > 2) { - paint.fillRect((px + ppx) / 2 + 1, - h - pkh, - x - (px + ppx) / 2 - 1, - pkh, - col); - } - } - - if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { - // black notes - paint.drawLine(x, h - pkh, x, h); - int rw = int(lrint(double(x - px) / 4) * 2); - if (rw < 2) rw = 2; - paint.drawRect(x - rw/2, h - pkh, rw, pkh/2); - } else if (n == 0 || n == 5) { - // C, F - if (px < w) { - paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h); - } - } - - ppx = px; - px = x; - } -// } + + paintHorizontalScale(v, paint, xorigin); paint.restore(); } +int +SpectrumLayer::getHorizontalScaleHeight(LayerGeometryProvider *v, + QPainter &paint) const +{ + int pkh = int(paint.fontMetrics().height() * 0.7 + 0.5); + if (pkh < 10) pkh = 10; + + int scaleh = HorizontalFrequencyScale().getHeight(v, paint); + + return pkh + scaleh; +} + +void +SpectrumLayer::paintHorizontalScale(LayerGeometryProvider *v, + QPainter &paint, + int xorigin) const +{ + //!!! All of this stuff relating to depicting frequencies + // (keyboard, crosshairs etc) should be applicable to any slice + // layer whose model has a vertical scale unit of Hz. However, + // the dense 3d model at the moment doesn't record its vertical + // scale unit -- we need to fix that and hoist this code as + // appropriate. Same really goes for any code in SpectrogramLayer + // that could be relevant to Colour3DPlotLayer with unit Hz, but + // that's a bigger proposition. + + if (!v->getViewManager()->shouldShowHorizontalValueScale()) { + return; + } + + int totalScaleHeight = getHorizontalScaleHeight(v, paint); // inc piano + int freqScaleHeight = HorizontalFrequencyScale().getHeight(v, paint); + int paintHeight = v->getPaintHeight(); + int paintWidth = v->getPaintWidth(); + + PianoScale().paintPianoHorizontal + (v, this, paint, + QRect(xorigin, paintHeight - totalScaleHeight - 1, + paintWidth - 1, totalScaleHeight - freqScaleHeight)); + + int scaleLeft = int(getXForBin(v, 1)); + + paint.drawLine(int(getXForBin(v, 0)), paintHeight - freqScaleHeight, + scaleLeft, paintHeight - freqScaleHeight); + + QString hz = tr("Hz"); + int hzw = paint.fontMetrics().width(hz); + if (scaleLeft > hzw + 5) { + paint.drawText + (scaleLeft - hzw - 5, + paintHeight - freqScaleHeight + paint.fontMetrics().ascent() + 5, + hz); + } + + HorizontalFrequencyScale().paintScale + (v, this, paint, + QRect(scaleLeft, paintHeight - freqScaleHeight, + paintWidth, totalScaleHeight), + m_binScale == LogBins); +} + void SpectrumLayer::getBiasCurve(BiasCurve &curve) const { diff -r 57d192e26331 -r 13d9b422f7fe layer/SpectrumLayer.h --- a/layer/SpectrumLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SpectrumLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -14,8 +14,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SPECTRUM_LAYER_H_ -#define _SPECTRUM_LAYER_H_ +#ifndef SV_SPECTRUM_LAYER_H +#define SV_SPECTRUM_LAYER_H #include "SliceLayer.h" @@ -23,12 +23,15 @@ #include "data/model/DenseTimeValueModel.h" +#include "HorizontalScaleProvider.h" + #include #include class FFTModel; -class SpectrumLayer : public SliceLayer +class SpectrumLayer : public SliceLayer, + public HorizontalScaleProvider { Q_OBJECT @@ -37,46 +40,46 @@ ~SpectrumLayer(); void setModel(DenseTimeValueModel *model); - virtual const Model *getModel() const { return m_originModel; } + virtual const Model *getModel() const override { return m_originModel; } virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos, - std::vector &extents) const; - virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const; + std::vector &extents) const override; + virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const override; - virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; + virtual int getHorizontalScaleHeight(LayerGeometryProvider *, QPainter &) const; + virtual void paintHorizontalScale(LayerGeometryProvider *, QPainter &, int xorigin) const; + + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override; - virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override; - virtual VerticalPosition getPreferredFrameCountPosition() const { - return PositionTop; + virtual VerticalPosition getPreferredFrameCountPosition() const override { + return PositionTop; } - virtual PropertyList getProperties() const; - virtual QString getPropertyLabel(const PropertyName &) const; - virtual QString getPropertyIconName(const PropertyName &) const; - virtual PropertyType getPropertyType(const PropertyName &) const; - virtual QString getPropertyGroupName(const PropertyName &) const; + virtual PropertyList getProperties() const override; + virtual QString getPropertyLabel(const PropertyName &) const override; + virtual QString getPropertyIconName(const PropertyName &) const override; + virtual PropertyType getPropertyType(const PropertyName &) const override; + virtual QString getPropertyGroupName(const PropertyName &) const override; virtual int getPropertyRangeAndValue(const PropertyName &, - int *min, int *max, int *deflt) const; + int *min, int *max, int *deflt) const override; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; - virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; - virtual void setProperty(const PropertyName &, int value); - virtual void setProperties(const QXmlAttributes &); - - virtual bool getValueExtents(double &min, double &max, - bool &logarithmic, QString &unit) const; + int value) const override; + virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const override; + virtual void setProperty(const PropertyName &, int value) override; + virtual void setProperties(const QXmlAttributes &) override; virtual bool getXScaleValue(const LayerGeometryProvider *v, int x, - double &value, QString &unit) const; + double &value, QString &unit) const override; virtual bool getYScaleValue(const LayerGeometryProvider *, int y, - double &value, QString &unit) const; + double &value, QString &unit) const override; virtual bool getYScaleDifference(const LayerGeometryProvider *, int y0, int y1, - double &diff, QString &unit) const; + double &diff, QString &unit) const override; - virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const override { return false; } void setChannel(int); int getChannel() const { return m_channel; } @@ -93,20 +96,18 @@ void setShowPeaks(bool); bool getShowPeaks() const { return m_showPeaks; } - virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } + virtual void toXml(QTextStream &stream, QString indent = "", + QString extraAttributes = "") const override; - virtual void toXml(QTextStream &stream, QString indent = "", - QString extraAttributes = "") const; + virtual double getFrequencyForX(const LayerGeometryProvider *, double x) + const override; + virtual double getXForFrequency(const LayerGeometryProvider *, double freq) + const override; protected slots: void preferenceChanged(PropertyContainer::PropertyName name); protected: - // make this SliceLayer method unavailable to the general public -// virtual void setModel(DenseThreeDimensionalModel *model) { -// SliceLayer::setModel(model); -// } - DenseTimeValueModel *m_originModel; int m_channel; bool m_channelSet; @@ -120,15 +121,9 @@ void setupFFT(); - virtual void getBiasCurve(BiasCurve &) const; + virtual void getBiasCurve(BiasCurve &) const override; BiasCurve m_biasCurve; - virtual double getXForBin(int bin, int totalBins, double w) const; - virtual int getBinForX(double x, int totalBins, double w) const; - - double getFrequencyForX(double x, double w) const; - double getXForFrequency(double freq, double w) const; - int getWindowIncrement() const { if (m_windowHopLevel == 0) return m_windowSize; else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4; diff -r 57d192e26331 -r 13d9b422f7fe layer/TextLayer.cpp --- a/layer/TextLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TextLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -77,14 +77,14 @@ int TextLayer::getPropertyRangeAndValue(const PropertyName &name, - int *min, int *max, int *deflt) const + int *min, int *max, int *deflt) const { return SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } QString TextLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -123,31 +123,31 @@ QFontMetrics metrics = QFontMetrics(QFont()); for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const TextModel::Point &p(*i); + const TextModel::Point &p(*i); - int px = v->getXForFrame(p.frame); - int py = getYForHeight(v, p.height); + int px = v->getXForFrame(p.frame); + int py = getYForHeight(v, p.height); - QString label = p.label; - if (label == "") { - label = tr(""); - } + QString label = p.label; + if (label == "") { + label = tr(""); + } - QRect rect = metrics.boundingRect - (QRect(0, 0, 150, 200), - Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); + QRect rect = metrics.boundingRect + (QRect(0, 0, 150, 200), + Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); - if (py + rect.height() > v->getPaintHeight()) { - if (rect.height() > v->getPaintHeight()) py = 0; - else py = v->getPaintHeight() - rect.height() - 1; - } + if (py + rect.height() > v->getPaintHeight()) { + if (rect.height() > v->getPaintHeight()) py = 0; + else py = v->getPaintHeight() - rect.height() - 1; + } - if (x >= px && x < px + rect.width() && - y >= py && y < py + rect.height()) { - rv.insert(p); - } + if (x >= px && x < px + rect.width() && + y >= py && y < py + rect.height()) { + rv.insert(p); + } } return rv; @@ -191,11 +191,11 @@ TextModel::PointList points = getLocalPoints(v, x, pos.y()); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return ""; - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return ""; + } } sv_frame_t useFrame = points.begin()->frame; @@ -205,14 +205,14 @@ QString text; if (points.begin()->label == "") { - 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); + 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); } pos = QPoint(v->getXForFrame(useFrame), - getYForHeight(v, points.begin()->height)); + getYForHeight(v, points.begin()->height)); return text; } @@ -221,22 +221,22 @@ bool TextLayer::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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); TextModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame), -1); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame), -1); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -244,47 +244,47 @@ bool found = false; for (TextModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } + if (i->frame > frame) { + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - TextModel::PointList::const_iterator j = i; - ++j; + TextModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -332,7 +332,7 @@ penColour = v->getForeground(); // SVDEBUG << "TextLayer::paint: resolution is " -// << m_model->getResolution() << " frames" << endl; +// << m_model->getResolution() << " frames" << endl; QPoint localPos; TextModel::Point illuminatePoint(0); @@ -350,58 +350,58 @@ paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight()); for (TextModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const TextModel::Point &p(*i); + const TextModel::Point &p(*i); - int x = v->getXForFrame(p.frame); - int y = getYForHeight(v, p.height); + int x = v->getXForFrame(p.frame); + int y = getYForHeight(v, p.height); if (!shouldIlluminate || // "illuminatePoint != p" TextModel::Point::Comparator()(illuminatePoint, p) || TextModel::Point::Comparator()(p, illuminatePoint)) { - paint.setPen(penColour); - paint.setBrush(brushColour); + paint.setPen(penColour); + paint.setBrush(brushColour); } else { - paint.setBrush(penColour); + paint.setBrush(penColour); paint.setPen(v->getBackground()); - } + } - QString label = p.label; - if (label == "") { - label = tr(""); - } + QString label = p.label; + if (label == "") { + label = tr(""); + } - QRect boxRect = paint.fontMetrics().boundingRect - (QRect(0, 0, boxMaxWidth, boxMaxHeight), - Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); + QRect boxRect = paint.fontMetrics().boundingRect + (QRect(0, 0, boxMaxWidth, boxMaxHeight), + Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); - QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); - boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); + QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); + boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); - if (y + boxRect.height() > v->getPaintHeight()) { - if (boxRect.height() > v->getPaintHeight()) y = 0; - else y = v->getPaintHeight() - boxRect.height() - 1; - } + if (y + boxRect.height() > v->getPaintHeight()) { + if (boxRect.height() > v->getPaintHeight()) y = 0; + else y = v->getPaintHeight() - boxRect.height() - 1; + } - boxRect = QRect(x, y, boxRect.width(), boxRect.height()); - textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); + boxRect = QRect(x, y, boxRect.width(), boxRect.height()); + textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); -// boxRect = QRect(x, y, boxRect.width(), boxRect.height()); -// textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); +// boxRect = QRect(x, y, boxRect.width(), boxRect.height()); +// textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); - paint.setRenderHint(QPainter::Antialiasing, false); - paint.drawRect(boxRect); + paint.setRenderHint(QPainter::Antialiasing, false); + paint.drawRect(boxRect); - paint.setRenderHint(QPainter::Antialiasing, true); - paint.drawText(textRect, - Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, - label); + paint.setRenderHint(QPainter::Antialiasing, true); + paint.drawText(textRect, + 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.label != "") { +/// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); +/// } } paint.restore(); @@ -416,8 +416,8 @@ // SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; if (!m_model) { - SVDEBUG << "TextLayer::drawStart: no model" << endl; - return; + SVDEBUG << "TextLayer::drawStart: no model" << endl; + return; } sv_frame_t frame = v->getFrameForX(e->x()); @@ -463,13 +463,13 @@ bool ok = false; QString label = QInputDialog::getText(v->getView(), tr("Enter label"), - tr("Please enter a new label:"), - QLineEdit::Normal, "", &ok); + tr("Please enter a new label:"), + QLineEdit::Normal, "", &ok); if (ok) { - TextModel::RelabelCommand *command = - new TextModel::RelabelCommand(m_model, m_editingPoint, label); - m_editingCommand->addCommand(command); + TextModel::RelabelCommand *command = + new TextModel::RelabelCommand(m_model, m_editingPoint, label); + m_editingCommand->addCommand(command); } else { m_editingCommand->deletePoint(m_editingPoint); } @@ -487,8 +487,8 @@ if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -535,8 +535,8 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -560,7 +560,7 @@ // double height = getHeightForY(v, e->y()); if (!m_editingCommand) { - m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); + m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); } m_editingCommand->deletePoint(m_editingPoint); @@ -577,20 +577,20 @@ if (m_editingCommand) { - QString newName = m_editingCommand->getName(); + QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.height != m_originalPoint.height) { - newName = tr("Move Label"); - } else { - newName = tr("Move Label Horizontally"); - } - } else { - newName = tr("Move Label Vertically"); - } + if (m_editingPoint.frame != m_originalPoint.frame) { + if (m_editingPoint.height != m_originalPoint.height) { + newName = tr("Move Label"); + } else { + newName = tr("Move Label Horizontally"); + } + } else { + newName = tr("Move Label Vertically"); + } - m_editingCommand->setName(newName); - finish(m_editingCommand); + m_editingCommand->setName(newName); + finish(m_editingCommand); } m_editingCommand = 0; @@ -609,12 +609,12 @@ bool ok = false; label = QInputDialog::getText(v->getView(), tr("Enter label"), - tr("Please enter a new label:"), - QLineEdit::Normal, label, &ok); + 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); + TextModel::RelabelCommand *command = + new TextModel::RelabelCommand(m_model, text, label); + CommandHistory::getInstance()->addCommand(command); } return true; @@ -626,20 +626,20 @@ if (!m_model) return; TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Drag Selection")); + new TextModel::EditCommand(m_model, tr("Drag Selection")); TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + 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); - } + if (s.contains(i->frame)) { + TextModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -651,29 +651,29 @@ if (!m_model) return; TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Resize Selection")); + new TextModel::EditCommand(m_model, tr("Resize Selection")); TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; + 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); - } + TextModel::Point newPoint(*i); + newPoint.frame = lrint(target); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -685,14 +685,14 @@ if (!m_model) return; TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Delete Selection")); + new TextModel::EditCommand(m_model, tr("Delete Selection")); TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + i != points.end(); ++i) { + if (s.contains(i->frame)) command->deletePoint(*i); } finish(command); @@ -704,11 +704,11 @@ if (!m_model) return; TextModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (TextModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -743,7 +743,7 @@ } TextModel::EditCommand *command = - new TextModel::EditCommand(m_model, tr("Paste")); + new TextModel::EditCommand(m_model, tr("Paste")); double valueMin = 0.0, valueMax = 1.0; for (Clipboard::PointList::const_iterator i = points.begin(); diff -r 57d192e26331 -r 13d9b422f7fe layer/TextLayer.h --- a/layer/TextLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TextLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -37,8 +37,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); @@ -71,7 +71,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; diff -r 57d192e26331 -r 13d9b422f7fe layer/TimeInstantLayer.cpp --- a/layer/TimeInstantLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TimeInstantLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -101,16 +101,16 @@ int val = 0; if (name == "Plot Type") { - - if (min) *min = 0; - if (max) *max = 1; + + if (min) *min = 0; + if (max) *max = 1; if (deflt) *deflt = 0; - - val = int(m_plotStyle); + + val = int(m_plotStyle); } else { - - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -121,11 +121,11 @@ int value) const { if (name == "Plot Type") { - switch (value) { - default: - case 0: return tr("Instants"); - case 1: return tr("Segmentation"); - } + switch (value) { + default: + case 0: return tr("Instants"); + case 1: return tr("Segmentation"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -134,7 +134,7 @@ TimeInstantLayer::setProperty(const PropertyName &name, int value) { if (name == "Plot Type") { - setPlotStyle(PlotStyle(value)); + setPlotStyle(PlotStyle(value)); } else { SingleColourLayer::setProperty(name, value); } @@ -167,36 +167,36 @@ sv_frame_t frame = v->getFrameForX(x); SparseOneDimensionalModel::PointList onPoints = - m_model->getPoints(frame); + m_model->getPoints(frame); if (!onPoints.empty()) { - return onPoints; + return onPoints; } SparseOneDimensionalModel::PointList prevPoints = - m_model->getPreviousPoints(frame); + m_model->getPreviousPoints(frame); SparseOneDimensionalModel::PointList nextPoints = - m_model->getNextPoints(frame); + m_model->getNextPoints(frame); SparseOneDimensionalModel::PointList usePoints = prevPoints; if (prevPoints.empty()) { - usePoints = nextPoints; + usePoints = nextPoints; } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; + !(nextPoints.begin()->frame > v->getEndFrame())) { + usePoints = nextPoints; } else if (nextPoints.begin()->frame - frame < - frame - prevPoints.begin()->frame) { - usePoints = nextPoints; + frame - prevPoints.begin()->frame) { + usePoints = nextPoints; } if (!usePoints.empty()) { - int fuzz = 2; - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 1)) { - usePoints.clear(); - } + int fuzz = 2; + int px = v->getXForFrame(usePoints.begin()->frame); + if ((px > x && px - x > fuzz) || + (px < x && x - px > fuzz + 1)) { + usePoints.clear(); + } } return usePoints; @@ -224,11 +224,11 @@ SparseOneDimensionalModel::PointList points = getLocalPoints(v, x); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return tr("No local points"); - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return tr("No local points"); + } } sv_frame_t useFrame = points.begin()->frame; @@ -238,12 +238,12 @@ QString text; if (points.begin()->label == "") { - text = QString(tr("Time:\t%1\nNo label")) - .arg(rt.toText(true).c_str()); + 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); + text = QString(tr("Time:\t%1\nLabel:\t%2")) + .arg(rt.toText(true).c_str()) + .arg(points.begin()->label); } pos = QPoint(v->getXForFrame(useFrame), pos.y()); @@ -252,22 +252,22 @@ bool TimeInstantLayer::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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); SparseOneDimensionalModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame)); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -275,47 +275,47 @@ bool found = false; for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame >= frame) { - snapped = i->frame; - found = true; - break; - } + if (i->frame >= frame) { + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - SparseOneDimensionalModel::PointList::const_iterator j = i; - ++j; + SparseOneDimensionalModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -335,12 +335,12 @@ sv_frame_t frame1 = v->getFrameForX(x1); SparseOneDimensionalModel::PointList points(m_model->getPoints - (frame0, frame1)); + (frame0, frame1)); bool odd = false; if (m_plotStyle == PlotSegmentation && !points.empty()) { - int index = m_model->getIndexOf(*points.begin()); - odd = ((index % 2) == 1); + int index = m_model->getIndexOf(*points.begin()); + odd = ((index % 2) == 1); } paint.setPen(getBaseQColor()); @@ -351,119 +351,121 @@ QColor oddBrushColour(brushColour); if (m_plotStyle == PlotSegmentation) { - if (getBaseQColor() == Qt::black) { - oddBrushColour = Qt::gray; - } else if (getBaseQColor() == Qt::darkRed) { - oddBrushColour = Qt::red; - } else if (getBaseQColor() == Qt::darkBlue) { - oddBrushColour = Qt::blue; - } else if (getBaseQColor() == Qt::darkGreen) { - oddBrushColour = Qt::green; - } else { - oddBrushColour = oddBrushColour.light(150); - } - oddBrushColour.setAlpha(100); + if (getBaseQColor() == Qt::black) { + oddBrushColour = Qt::gray; + } else if (getBaseQColor() == Qt::darkRed) { + oddBrushColour = Qt::red; + } else if (getBaseQColor() == Qt::darkBlue) { + oddBrushColour = Qt::blue; + } else if (getBaseQColor() == Qt::darkGreen) { + oddBrushColour = Qt::green; + } else { + oddBrushColour = oddBrushColour.light(150); + } + oddBrushColour.setAlpha(100); } // SVDEBUG << "TimeInstantLayer::paint: resolution is " -// << m_model->getResolution() << " frames" << endl; +// << m_model->getResolution() << " frames" << endl; QPoint localPos; sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { - SparseOneDimensionalModel::PointList localPoints = - getLocalPoints(v, localPos.x()); - if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; + SparseOneDimensionalModel::PointList localPoints = + getLocalPoints(v, localPos.x()); + if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; } - + int prevX = -1; int textY = v->getTextLabelHeight(this, paint); for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - const SparseOneDimensionalModel::Point &p(*i); - SparseOneDimensionalModel::PointList::const_iterator j = i; - ++j; + const SparseOneDimensionalModel::Point &p(*i); + SparseOneDimensionalModel::PointList::const_iterator j = i; + ++j; - int x = v->getXForFrame(p.frame); + int x = v->getXForFrame(p.frame); if (x == prevX && m_plotStyle == PlotInstants && p.frame != illuminateFrame) continue; - int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x; - if (iw < 2) { - if (iw < 1) { - iw = 2; - if (j != points.end()) { - int nx = v->getXForFrame(j->frame); - if (nx < x + 3) iw = 1; - } - } else { - iw = 2; - } - } - - if (p.frame == illuminateFrame) { - paint.setPen(getForegroundQColor(v->getView())); - } else { - paint.setPen(brushColour); - } + int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x; + if (iw < 2) { + if (iw < 1) { + iw = 2; + if (j != points.end()) { + int nx = v->getXForFrame(j->frame); + if (nx < x + 3) iw = 1; + } + } else { + iw = 2; + } + } + + if (p.frame == illuminateFrame) { + paint.setPen(getForegroundQColor(v->getView())); + } else { + paint.setPen(brushColour); + } - if (m_plotStyle == PlotInstants) { - if (iw > 1) { - paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1); - } else { - paint.drawLine(x, 0, x, v->getPaintHeight() - 1); - } - } else { + if (m_plotStyle == PlotInstants) { + if (iw > 1) { + paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1); + } else { + paint.drawLine(x, 0, x, v->getPaintHeight() - 1); + } + } else { - if (odd) paint.setBrush(oddBrushColour); - else paint.setBrush(brushColour); - - int nx; - - if (j != points.end()) { - const SparseOneDimensionalModel::Point &q(*j); - nx = v->getXForFrame(q.frame); - } else { - nx = v->getXForFrame(m_model->getEndFrame()); - } + if (odd) paint.setBrush(oddBrushColour); + else paint.setBrush(brushColour); + + int nx; + + if (j != points.end()) { + const SparseOneDimensionalModel::Point &q(*j); + nx = v->getXForFrame(q.frame); + } else { + nx = v->getXForFrame(m_model->getEndFrame()); + } - if (nx >= x) { - - if (illuminateFrame != p.frame && - (nx < x + 5 || x >= v->getPaintWidth() - 1)) { - paint.setPen(Qt::NoPen); - } + if (nx >= x) { + + if (illuminateFrame != p.frame && + (nx < x + 5 || x >= v->getPaintWidth() - 1)) { + paint.setPen(Qt::NoPen); + } paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1); - } + } - odd = !odd; - } + odd = !odd; + } - paint.setPen(getBaseQColor()); - - if (p.label != "") { + paint.setPen(getBaseQColor()); + + if (p.label != "") { - // only draw if there's enough room from here to the next point + // only draw if there's enough room from here to the next point - int lw = paint.fontMetrics().width(p.label); - bool good = true; + int lw = paint.fontMetrics().width(p.label); + bool good = true; - if (j != points.end()) { - int nx = v->getXForFrame(j->frame); - if (nx >= x && nx - x - iw - 3 <= lw) good = false; - } + if (j != points.end()) { + int nx = v->getXForFrame(j->frame); + if (nx >= x && nx - x - iw - 3 <= lw) good = false; + } - if (good) { - PaintAssistant::drawVisibleText(v, paint, x + iw + 2, textY, p.label, PaintAssistant::OutlinedText); -// paint.drawText(x + iw + 2, textY, p.label); - } - } + if (good) { + PaintAssistant::drawVisibleText(v, paint, + x + iw + 2, textY, + p.label, + PaintAssistant::OutlinedText); + } + } - prevX = x; + prevX = x; } } @@ -484,7 +486,7 @@ if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, - tr("Draw Point")); + tr("Draw Point")); m_editingCommand->addPoint(m_editingPoint); m_editing = true; @@ -515,9 +517,9 @@ #endif if (!m_model || !m_editing) return; QString newName = tr("Add Point at %1 s") - .arg(RealTime::frame2RealTime(m_editingPoint.frame, - m_model->getSampleRate()) - .toText(false).c_str()); + .arg(RealTime::frame2RealTime(m_editingPoint.frame, + m_model->getSampleRate()) + .toText(false).c_str()); m_editingCommand->setName(newName); finish(m_editingCommand); m_editingCommand = 0; @@ -535,8 +537,8 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -583,8 +585,8 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -604,8 +606,8 @@ frame = frame / m_model->getResolution() * m_model->getResolution(); if (!m_editingCommand) { - m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, + tr("Drag Point")); } m_editingCommand->deletePoint(m_editingPoint); @@ -621,12 +623,12 @@ #endif if (!m_model || !m_editing) return; if (m_editingCommand) { - QString newName = tr("Move Point to %1 s") - .arg(RealTime::frame2RealTime(m_editingPoint.frame, - m_model->getSampleRate()) - .toText(false).c_str()); - m_editingCommand->setName(newName); - finish(m_editingCommand); + QString newName = tr("Move Point to %1 s") + .arg(RealTime::frame2RealTime(m_editingPoint.frame, + m_model->getSampleRate()) + .toText(false).c_str()); + m_editingCommand->setName(newName); + finish(m_editingCommand); } m_editingCommand = 0; m_editing = false; @@ -673,21 +675,21 @@ if (!m_model) return; SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Drag Selection")); + new SparseOneDimensionalModel::EditCommand(m_model, + tr("Drag Selection")); SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + 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); - } + if (s.contains(i->frame)) { + SparseOneDimensionalModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -699,30 +701,30 @@ if (!m_model) return; SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Resize Selection")); + new SparseOneDimensionalModel::EditCommand(m_model, + tr("Resize Selection")); SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; + 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); - } + SparseOneDimensionalModel::Point newPoint(*i); + newPoint.frame = lrint(target); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -734,15 +736,15 @@ if (!m_model) return; SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, - tr("Delete Selection")); + new SparseOneDimensionalModel::EditCommand(m_model, + tr("Delete Selection")); SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) command->deletePoint(*i); + i != points.end(); ++i) { + if (s.contains(i->frame)) command->deletePoint(*i); } finish(command); @@ -754,11 +756,11 @@ if (!m_model) return; SparseOneDimensionalModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseOneDimensionalModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -793,7 +795,7 @@ } SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste")); + new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste")); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -861,7 +863,7 @@ bool ok; PlotStyle style = (PlotStyle) - attributes.value("plotStyle").toInt(&ok); + attributes.value("plotStyle").toInt(&ok); if (ok) setPlotStyle(style); } diff -r 57d192e26331 -r 13d9b422f7fe layer/TimeInstantLayer.h --- a/layer/TimeInstantLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TimeInstantLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -39,8 +39,8 @@ virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); @@ -73,12 +73,12 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); enum PlotStyle { - PlotInstants, - PlotSegmentation + PlotInstants, + PlotSegmentation }; void setPlotStyle(PlotStyle style); diff -r 57d192e26331 -r 13d9b422f7fe layer/TimeRulerLayer.cpp --- a/layer/TimeRulerLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TimeRulerLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -167,28 +167,28 @@ quarterTicks = false; if (rtGap.sec > 0) { - incms = 1000; - int s = rtGap.sec; - if (s > 0) { incms *= 5; s /= 5; } - if (s > 0) { incms *= 2; s /= 2; } - if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; } - if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; } - if (s > 0) { incms *= 2; s /= 2; } - if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; } - while (s > 0) { - incms *= 10; - s /= 10; - quarterTicks = false; - } + incms = 1000; + int s = rtGap.sec; + if (s > 0) { incms *= 5; s /= 5; } + if (s > 0) { incms *= 2; s /= 2; } + if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; } + if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; } + if (s > 0) { incms *= 2; s /= 2; } + if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; } + while (s > 0) { + incms *= 10; + s /= 10; + quarterTicks = false; + } } else { - incms = 1; - int ms = rtGap.msec(); + incms = 1; + int ms = rtGap.msec(); // cerr << "rtGap.msec = " << ms << ", rtGap = " << rtGap << ", count = " << count << endl; // cerr << "startFrame = " << startFrame << ", endFrame = " << endFrame << " rtStart = " << rtStart << ", rtEnd = " << rtEnd << endl; - if (ms > 0) { incms *= 10; ms /= 10; } - if (ms > 0) { incms *= 10; ms /= 10; } - if (ms > 0) { incms *= 5; ms /= 5; } - if (ms > 0) { incms *= 2; ms /= 2; } + if (ms > 0) { incms *= 10; ms /= 10; } + if (ms > 0) { incms *= 10; ms /= 10; } + if (ms > 0) { incms *= 5; ms /= 5; } + if (ms > 0) { incms *= 2; ms /= 2; } } return incms; @@ -199,7 +199,7 @@ { #ifdef DEBUG_TIME_RULER_LAYER SVDEBUG << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y() - << ") [" << rect.width() << "x" << rect.height() << "]" << endl; + << ") [" << rect.width() << "x" << rect.height() << "]" << endl; #endif if (!m_model || !m_model->isOK()) return; @@ -233,7 +233,7 @@ int incX = int(incFrame / v->getZoomLevel()); int ticks = 10; if (incX < minPixelSpacing * 2) { - ticks = quarter ? 4 : 5; + ticks = quarter ? 4 : 5; } QColor greyColour = getPartialShades(v)[1]; @@ -279,7 +279,7 @@ break; } - if (x >= rect.x() - 50 && ms >= minlabel) { + if (x >= rect.x() - 50 && ms >= minlabel) { RealTime rt = RealTime::fromMilliseconds(ms); @@ -331,9 +331,9 @@ } } - paint.setPen(greyColour); + paint.setPen(greyColour); - for (int i = 1; i < ticks; ++i) { + for (int i = 1; i < ticks; ++i) { dms = ms + (i * double(incms)) / ticks; frame = lrint((dms * sampleRate) / 1000.0); @@ -353,21 +353,21 @@ cerr << "tick " << i << " in range, drawing at " << x << endl; #endif - int sz = 5; - if (ticks == 10) { - if ((i % 2) == 1) { - if (i == 5) { - paint.drawLine(x, 0, x, v->getPaintHeight()); - } else sz = 3; - } else { - sz = 7; - } - } - paint.drawLine(x, 0, x, sz); - paint.drawLine(x, v->getPaintHeight() - sz - 1, x, v->getPaintHeight() - 1); - } + int sz = 5; + if (ticks == 10) { + if ((i % 2) == 1) { + if (i == 5) { + paint.drawLine(x, 0, x, v->getPaintHeight()); + } else sz = 3; + } else { + sz = 7; + } + } + paint.drawLine(x, 0, x, sz); + paint.drawLine(x, v->getPaintHeight() - sz - 1, x, v->getPaintHeight() - 1); + } - ms += incms; + ms += incms; } paint.restore(); diff -r 57d192e26331 -r 13d9b422f7fe layer/TimeValueLayer.cpp --- a/layer/TimeValueLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TimeValueLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -132,7 +132,7 @@ if (name == "Plot Type") return ValueProperty; if (name == "Vertical Scale") return ValueProperty; if (name == "Scale Units") return UnitsProperty; - if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty; + if (name == "Colour" && m_plotStyle == PlotSegmentation) return ColourMapProperty; if (name == "Draw Segment Division Lines") return ToggleProperty; if (name == "Show Derivative") return ToggleProperty; return SingleColourLayer::getPropertyType(name); @@ -160,7 +160,7 @@ int TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name, - int *min, int *max, int *deflt) const + int *min, int *max, int *deflt) const { int val = 0; @@ -173,20 +173,20 @@ val = m_colourMap; } else if (name == "Plot Type") { - - if (min) *min = 0; - if (max) *max = 6; + + if (min) *min = 0; + if (max) *max = 6; if (deflt) *deflt = int(PlotConnectedPoints); - - val = int(m_plotStyle); + + val = int(m_plotStyle); } else if (name == "Vertical Scale") { - - if (min) *min = 0; - if (max) *max = 3; + + if (min) *min = 0; + if (max) *max = 3; if (deflt) *deflt = int(AutoAlignScale); - - val = int(m_verticalScale); + + val = int(m_verticalScale); } else if (name == "Scale Units") { @@ -211,8 +211,8 @@ val = (m_derivative ? 1.0 : 0.0); } else { - - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -220,29 +220,29 @@ QString TimeValueLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Colour" && m_plotStyle == PlotSegmentation) { return ColourMapper::getColourMapName(value); } else if (name == "Plot Type") { - switch (value) { - default: - case 0: return tr("Points"); - case 1: return tr("Stems"); - case 2: return tr("Connected Points"); - case 3: return tr("Lines"); - case 4: return tr("Curve"); - case 5: return tr("Segmentation"); - case 6: return tr("Discrete Curves"); - } + switch (value) { + default: + case 0: return tr("Points"); + case 1: return tr("Stems"); + case 2: return tr("Connected Points"); + case 3: return tr("Lines"); + case 4: return tr("Curve"); + case 5: return tr("Segmentation"); + case 6: return tr("Discrete Curves"); + } } else if (name == "Vertical Scale") { - switch (value) { - default: - case 0: return tr("Auto-Align"); - case 1: return tr("Linear"); - case 2: return tr("Log"); - case 3: return tr("+/-1"); - } + switch (value) { + default: + case 0: return tr("Auto-Align"); + case 1: return tr("Linear"); + case 2: return tr("Log"); + case 3: return tr("+/-1"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -253,9 +253,9 @@ if (name == "Colour" && m_plotStyle == PlotSegmentation) { setFillColourMap(value); } else if (name == "Plot Type") { - setPlotStyle(PlotStyle(value)); + setPlotStyle(PlotStyle(value)); } else if (name == "Vertical Scale") { - setVerticalScale(VerticalScale(value)); + setVerticalScale(VerticalScale(value)); } else if (name == "Scale Units") { if (m_model) { m_model->setScaleUnits @@ -323,7 +323,7 @@ // they're always scrollable if (m_plotStyle == PlotLines || - m_plotStyle == PlotCurve || + m_plotStyle == PlotCurve || m_plotStyle == PlotDiscreteCurves) return true; QPoint discard; @@ -538,38 +538,38 @@ sv_frame_t frame = v->getFrameForX(x); SparseTimeValueModel::PointList onPoints = - m_model->getPoints(frame); + m_model->getPoints(frame); if (!onPoints.empty()) { - return onPoints; + return onPoints; } SparseTimeValueModel::PointList prevPoints = - m_model->getPreviousPoints(frame); + m_model->getPreviousPoints(frame); SparseTimeValueModel::PointList nextPoints = - m_model->getNextPoints(frame); + m_model->getNextPoints(frame); SparseTimeValueModel::PointList usePoints = prevPoints; if (prevPoints.empty()) { - usePoints = nextPoints; + usePoints = nextPoints; } else if (nextPoints.empty()) { // stick with prevPoints } else if (prevPoints.begin()->frame < v->getStartFrame() && - !(nextPoints.begin()->frame > v->getEndFrame())) { - usePoints = nextPoints; + !(nextPoints.begin()->frame > v->getEndFrame())) { + usePoints = nextPoints; } else if (nextPoints.begin()->frame - frame < - frame - prevPoints.begin()->frame) { - usePoints = nextPoints; + frame - prevPoints.begin()->frame) { + usePoints = nextPoints; } if (!usePoints.empty()) { - int fuzz = 2; - int px = v->getXForFrame(usePoints.begin()->frame); - if ((px > x && px - x > fuzz) || - (px < x && x - px > fuzz + 3)) { - usePoints.clear(); - } + int fuzz = 2; + int px = v->getXForFrame(usePoints.begin()->frame); + if ((px > x && px - x > fuzz) || + (px < x && x - px > fuzz + 3)) { + usePoints.clear(); + } } return usePoints; @@ -597,11 +597,11 @@ SparseTimeValueModel::PointList points = getLocalPoints(v, x); if (points.empty()) { - if (!m_model->isReady()) { - return tr("In progress"); - } else { - return tr("No local points"); - } + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return tr("No local points"); + } } sv_frame_t useFrame = points.begin()->frame; @@ -626,39 +626,39 @@ QString text; if (points.begin()->label == "") { - text = QString(tr("Time:\t%1\nValue:\t%2\nNo label")) - .arg(rt.toText(true).c_str()) - .arg(valueText); + text = QString(tr("Time:\t%1\nValue:\t%2\nNo label")) + .arg(rt.toText(true).c_str()) + .arg(valueText); } else { - text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4")) - .arg(rt.toText(true).c_str()) - .arg(valueText) - .arg(points.begin()->label); + text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4")) + .arg(rt.toText(true).c_str()) + .arg(valueText) + .arg(points.begin()->label); } pos = QPoint(v->getXForFrame(useFrame), - getYForValue(v, points.begin()->value)); + getYForValue(v, points.begin()->value)); return text; } bool TimeValueLayer::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); + return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); SparseTimeValueModel::PointList points; if (snap == SnapNeighbouring) { - - points = getLocalPoints(v, v->getXForFrame(frame)); - if (points.empty()) return false; - frame = points.begin()->frame; - return true; + + points = getLocalPoints(v, v->getXForFrame(frame)); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; } points = m_model->getPoints(frame, frame); @@ -666,47 +666,47 @@ bool found = false; for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > frame) { - snapped = i->frame; - found = true; - break; - } + if (i->frame > frame) { + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame <= frame) { - snapped = i->frame; - found = true; // don't break, as the next may be better - } else { - break; - } + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } - } else { // nearest + } else { // nearest - SparseTimeValueModel::PointList::const_iterator j = i; - ++j; + SparseTimeValueModel::PointList::const_iterator j = i; + ++j; - if (j == points.end()) { + if (j == points.end()) { - snapped = i->frame; - found = true; - break; + snapped = i->frame; + found = true; + break; - } else if (j->frame >= frame) { + } else if (j->frame >= frame) { - if (j->frame - frame < frame - i->frame) { - snapped = j->frame; - } else { - snapped = i->frame; - } - found = true; - break; - } - } + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } } frame = snapped; @@ -719,7 +719,7 @@ SnapType snap) const { if (!m_model) { - return Layer::snapToSimilarFeature(v, frame, resolution, snap); + return Layer::snapToSimilarFeature(v, frame, resolution, snap); } resolution = m_model->getResolution(); @@ -763,29 +763,29 @@ } } - if (snap == SnapRight) { + if (snap == SnapRight) { - if (i->frame > matchframe && + if (i->frame > matchframe && fabs(i->value - matchvalue) < epsilon) { - snapped = i->frame; - found = true; - break; - } + snapped = i->frame; + found = true; + break; + } - } else if (snap == SnapLeft) { + } else if (snap == SnapLeft) { - if (i->frame < matchframe) { + 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 if (found || distant) { + break; + } - } else { + } else { // no other snap types supported - } + } ++i; } @@ -926,7 +926,7 @@ if (m_derivative) --frame0; SparseTimeValueModel::PointList points(m_model->getPoints - (frame0, frame1)); + (frame0, frame1)); if (points.empty()) return; paint.setPen(getBaseQColor()); @@ -937,7 +937,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(); @@ -945,23 +945,23 @@ if (max == min) max = min + 1.0; int origin = int(nearbyint(v->getPaintHeight() - - (-min * v->getPaintHeight()) / (max - min))); + (-min * v->getPaintHeight()) / (max - min))); QPoint localPos; sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { - SparseTimeValueModel::PointList localPoints = - getLocalPoints(v, localPos.x()); + SparseTimeValueModel::PointList 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()->frame; } int w = - v->getXForFrame(frame0 + m_model->getResolution()) - - v->getXForFrame(frame0); + v->getXForFrame(frame0 + m_model->getResolution()) - + v->getXForFrame(frame0); if (m_plotStyle == PlotStems) { if (w < 2) w = 2; @@ -990,11 +990,11 @@ sv_frame_t prevFrame = 0; for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { if (m_derivative && i == points.begin()) continue; - const SparseTimeValueModel::Point &p(*i); + const SparseTimeValueModel::Point &p(*i); double value = p.value; if (m_derivative) { @@ -1003,8 +1003,8 @@ value -= j->value; } - int x = v->getXForFrame(p.frame); - int y = getYForValue(v, value); + int x = v->getXForFrame(p.frame); + int y = getYForValue(v, value); bool gap = false; if (m_plotStyle == PlotDiscreteCurves) { @@ -1024,81 +1024,74 @@ } } - bool haveNext = false; + bool haveNext = false; double nvalue = 0.f; sv_frame_t nf = v->getModelsEndFrame(); - int nx = v->getXForFrame(nf); - int ny = y; + int nx = v->getXForFrame(nf); + int ny = y; - SparseTimeValueModel::PointList::const_iterator j = i; - ++j; + SparseTimeValueModel::PointList::const_iterator j = i; + ++j; - if (j != points.end()) { - const SparseTimeValueModel::Point &q(*j); + if (j != points.end()) { + const SparseTimeValueModel::Point &q(*j); nvalue = q.value; if (m_derivative) nvalue -= p.value; nf = q.frame; - nx = v->getXForFrame(nf); - ny = getYForValue(v, nvalue); - haveNext = true; + nx = v->getXForFrame(nf); + ny = getYForValue(v, nvalue); + haveNext = true; } // cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext // << ", nx = " << nx << endl; + QPen pen(getBaseQColor()); + QBrush brush(brushColour); + if (m_plotStyle == PlotDiscreteCurves) { - paint.setPen(QPen(getBaseQColor(), 3)); - paint.setBrush(Qt::NoBrush); + pen = QPen(getBaseQColor(), 3); + brush = QBrush(Qt::NoBrush); } else if (m_plotStyle == PlotSegmentation) { - paint.setPen(getForegroundQColor(v)); - paint.setBrush(getColourForValue(v, value)); - } else if (m_plotStyle == PlotLines || - m_plotStyle == PlotCurve) { - paint.setPen(getBaseQColor()); - paint.setBrush(Qt::NoBrush); - } else { - paint.setPen(getBaseQColor()); - paint.setBrush(brushColour); - } - - if (m_plotStyle == PlotStems) { -/* - paint.setPen(brushColour); - if (y < origin - 1) { - paint.drawRect(x + w/2, y + 1, 1, origin - y); - } else if (y > origin + 1) { - paint.drawRect(x + w/2, origin, 1, y - origin - 1); - } -*/ - paint.setPen(getBaseQColor()); - if (y < origin - 1) { - paint.drawLine(x + w/2, y + 1, x + w/2, origin); - } else if (y > origin + 1) { - paint.drawLine(x + w/2, origin, x + w/2, y - 1); - } - } + pen = QPen(getForegroundQColor(v)); + brush = QBrush(getColourForValue(v, value)); + } else if (m_plotStyle == PlotLines || + m_plotStyle == PlotCurve) { + brush = QBrush(Qt::NoBrush); + } + + paint.setPen(PaintAssistant::scalePen(pen)); + paint.setBrush(brush); + + if (m_plotStyle == PlotStems) { + if (y < origin - 1) { + paint.drawLine(x + w/2, y + 1, x + w/2, origin); + } else if (y > origin + 1) { + paint.drawLine(x + w/2, origin, x + w/2, y - 1); + } + } bool illuminate = false; - if (illuminateFrame == p.frame) { + if (illuminateFrame == p.frame) { - // not equipped to illuminate the right section in line - // or curve mode + // not equipped to illuminate the right section in line + // or curve mode - if (m_plotStyle != PlotCurve && + if (m_plotStyle != PlotCurve && m_plotStyle != PlotDiscreteCurves && - m_plotStyle != PlotLines) { + m_plotStyle != PlotLines) { illuminate = true; } } - if (m_plotStyle != PlotLines && - m_plotStyle != PlotCurve && + if (m_plotStyle != PlotLines && + m_plotStyle != PlotCurve && m_plotStyle != PlotDiscreteCurves && - m_plotStyle != PlotSegmentation) { + m_plotStyle != PlotSegmentation) { if (illuminate) { paint.save(); - paint.setPen(getForegroundQColor(v)); + paint.setPen(PaintAssistant::scalePen(getForegroundQColor(v))); paint.setBrush(getForegroundQColor(v)); } if (m_plotStyle != PlotStems || @@ -1108,38 +1101,38 @@ if (illuminate) { paint.restore(); } - } + } - if (m_plotStyle == PlotConnectedPoints || - m_plotStyle == PlotLines || + if (m_plotStyle == PlotConnectedPoints || + m_plotStyle == PlotLines || m_plotStyle == PlotDiscreteCurves || - m_plotStyle == PlotCurve) { + m_plotStyle == PlotCurve) { - if (haveNext) { + if (haveNext) { - if (m_plotStyle == PlotConnectedPoints) { - + if (m_plotStyle == PlotConnectedPoints) { + paint.save(); - paint.setPen(brushColour); - paint.drawLine(x + w, y, nx, ny); + paint.setPen(PaintAssistant::scalePen(brushColour)); + paint.drawLine(x + w, y, nx, ny); paint.restore(); - } else if (m_plotStyle == PlotLines) { + } else if (m_plotStyle == PlotLines) { if (pointCount == 0) { path.moveTo(x + w/2, y); } -// paint.drawLine(x + w/2, y, nx + w/2, ny); +// paint.drawLine(x + w/2, y, nx + w/2, ny); path.lineTo(nx + w/2, ny); - } else { + } else { - double x0 = x + double(w)/2; - double x1 = nx + double(w)/2; - - double y0 = y; - double y1 = ny; + double x0 = x + double(w)/2; + double x1 = nx + double(w)/2; + + double y0 = y; + double y1 = ny; if (m_plotStyle == PlotDiscreteCurves) { bool nextGap = @@ -1151,35 +1144,35 @@ } } - if (pointCount == 0 || gap) { - path.moveTo((x0 + x1) / 2, (y0 + y1) / 2); - } + if (pointCount == 0 || gap) { + path.moveTo((x0 + x1) / 2, (y0 + y1) / 2); + } - if (nx - x > 5) { - path.cubicTo(x0, y0, - x0, y0, - (x0 + x1) / 2, (y0 + y1) / 2); + if (nx - x > 5) { + path.cubicTo(x0, y0, + x0, y0, + (x0 + x1) / 2, (y0 + y1) / 2); - // // or - // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); + // // or + // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); - } else { + } else { path.lineTo(x0, y0); - path.lineTo((x0 + x1) / 2, (y0 + y1) / 2); - } - } - } - } + path.lineTo((x0 + x1) / 2, (y0 + y1) / 2); + } + } + } + } - if (m_plotStyle == PlotSegmentation) { + if (m_plotStyle == PlotSegmentation) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "drawing rect" << endl; #endif - - if (nx <= x) continue; + + if (nx <= x) continue; - paint.setPen(QPen(getForegroundQColor(v), 2)); + paint.setPen(PaintAssistant::scalePen(QPen(getForegroundQColor(v), 2))); if (!illuminate) { if (!m_drawSegmentDivisions || @@ -1187,10 +1180,10 @@ x >= v->getPaintWidth() - 1) { paint.setPen(Qt::NoPen); } - } + } - paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1); - } + paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1); + } if (v->shouldShowFeatureLabels()) { @@ -1229,11 +1222,11 @@ if (m_plotStyle == PlotDiscreteCurves) { paint.setRenderHint(QPainter::Antialiasing, true); - paint.drawPath(path); + paint.drawPath(path); } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines) && !path.isEmpty()) { - paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth()); - paint.drawPath(path); + paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth()); + paint.drawPath(path); } paint.restore(); @@ -1245,7 +1238,9 @@ int TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { - if (!m_model || shouldAutoAlign()) { + if (!m_model) { + return 0; + } else if (shouldAutoAlign() && !valueExtentsMatchMine(v)) { return 0; } else if (m_plotStyle == PlotSegmentation) { if (m_verticalScale == LogScale) { @@ -1356,7 +1351,7 @@ if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, - tr("Draw Point")); + tr("Draw Point")); if (!havePoint) { m_editingCommand->addPoint(m_editingPoint); } @@ -1449,8 +1444,8 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -1499,8 +1494,8 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - finish(m_editingCommand); - m_editingCommand = 0; + finish(m_editingCommand); + m_editingCommand = 0; } m_editing = true; @@ -1522,8 +1517,8 @@ double value = getValueForY(v, e->y()); if (!m_editingCommand) { - m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, - tr("Drag Point")); + m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, + tr("Drag Point")); } m_editingCommand->deletePoint(m_editingPoint); @@ -1542,20 +1537,20 @@ if (m_editingCommand) { - QString newName = m_editingCommand->getName(); + QString newName = m_editingCommand->getName(); - if (m_editingPoint.frame != m_originalPoint.frame) { - if (m_editingPoint.value != m_originalPoint.value) { - newName = tr("Edit Point"); - } else { - newName = tr("Relocate Point"); - } - } else { - newName = tr("Change Point Value"); - } + if (m_editingPoint.frame != m_originalPoint.frame) { + if (m_editingPoint.value != m_originalPoint.value) { + newName = tr("Edit Point"); + } else { + newName = tr("Relocate Point"); + } + } else { + newName = tr("Change Point Value"); + } - m_editingCommand->setName(newName); - finish(m_editingCommand); + m_editingCommand->setName(newName); + finish(m_editingCommand); } m_editingCommand = 0; @@ -1607,21 +1602,21 @@ if (!m_model) return; SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Drag Selection")); + new SparseTimeValueModel::EditCommand(m_model, + tr("Drag Selection")); SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { - SparseTimeValueModel::Point newPoint(*i); - newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); - command->deletePoint(*i); - command->addPoint(newPoint); - } + if (s.contains(i->frame)) { + SparseTimeValueModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1633,30 +1628,30 @@ if (!m_model) return; SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Resize Selection")); + new SparseTimeValueModel::EditCommand(m_model, + tr("Resize Selection")); SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); double ratio = - double(newSize.getEndFrame() - newSize.getStartFrame()) / - double(s.getEndFrame() - s.getStartFrame()); + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { - if (s.contains(i->frame)) { + if (s.contains(i->frame)) { - double target = double(i->frame); - target = double(newSize.getStartFrame()) + - target - double(s.getStartFrame()) * ratio; + 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); - } + SparseTimeValueModel::Point newPoint(*i); + newPoint.frame = lrint(target); + command->deletePoint(*i); + command->addPoint(newPoint); + } } finish(command); @@ -1668,14 +1663,14 @@ if (!m_model) return; SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, - tr("Delete Selected Points")); + new SparseTimeValueModel::EditCommand(m_model, + tr("Delete Selected Points")); SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { + i != points.end(); ++i) { if (s.contains(i->frame)) { command->deletePoint(*i); @@ -1691,11 +1686,11 @@ if (!m_model) return; SparseTimeValueModel::PointList points = - m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); for (SparseTimeValueModel::PointList::iterator i = points.begin(); - i != points.end(); ++i) { - if (s.contains(i->frame)) { + 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); @@ -1731,7 +1726,7 @@ } SparseTimeValueModel::EditCommand *command = - new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); + new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); enum ValueAvailability { UnknownAvailability, @@ -1935,11 +1930,11 @@ if (ok) setFillColourMap(cmap); PlotStyle style = (PlotStyle) - attributes.value("plotStyle").toInt(&ok); + attributes.value("plotStyle").toInt(&ok); if (ok) setPlotStyle(style); VerticalScale scale = (VerticalScale) - attributes.value("verticalScale").toInt(&ok); + attributes.value("verticalScale").toInt(&ok); if (ok) setVerticalScale(scale); bool draw = (attributes.value("drawDivisions").trimmed() == "true"); diff -r 57d192e26331 -r 13d9b422f7fe layer/TimeValueLayer.h --- a/layer/TimeValueLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/TimeValueLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _TIME_VALUE_LAYER_H_ -#define _TIME_VALUE_LAYER_H_ +#ifndef SV_TIME_VALUE_LAYER_H +#define SV_TIME_VALUE_LAYER_H #include "SingleColourLayer.h" #include "VerticalScaleLayer.h" @@ -46,8 +46,8 @@ virtual QString getLabelPreceding(sv_frame_t) const; virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, - int &resolution, - SnapType snap) const; + int &resolution, + SnapType snap) const; virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap) const; @@ -85,19 +85,19 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); void setFillColourMap(int); int getFillColourMap() const { return m_colourMap; } enum PlotStyle { - PlotPoints, - PlotStems, - PlotConnectedPoints, - PlotLines, - PlotCurve, - PlotSegmentation, + PlotPoints, + PlotStems, + PlotConnectedPoints, + PlotLines, + PlotCurve, + PlotSegmentation, PlotDiscreteCurves }; @@ -146,6 +146,7 @@ void setProperties(const QXmlAttributes &attributes); + /// Override from SingleColourLayer virtual ColourSignificance getLayerColourSignificance() const { if (m_plotStyle == PlotSegmentation) { return ColourHasMeaningfulValue; @@ -154,6 +155,15 @@ } } + /// Override from SingleColourLayer + virtual bool hasLightBackground() const { + if (m_plotStyle == PlotSegmentation) { + return true; + } else { + return SingleColourLayer::hasLightBackground(); + } + } + /// VerticalScaleLayer and ColourScaleLayer methods virtual int getYForValue(LayerGeometryProvider *, double value) const; virtual double getValueForY(LayerGeometryProvider *, int y) const; diff -r 57d192e26331 -r 13d9b422f7fe layer/WaveformLayer.cpp --- a/layer/WaveformLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/WaveformLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -135,7 +135,7 @@ { if (name == "Gain" || name == "Normalize Visible Area" || - name == "Scale") return tr("Scale"); + name == "Scale") return tr("Scale"); return QString(); } @@ -152,13 +152,13 @@ if (name == "Gain") { - *min = -50; - *max = 50; + *min = -50; + *max = 50; *deflt = 0; - val = int(lrint(log10(m_gain) * 20.0)); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(log10(m_gain) * 20.0)); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Normalize Visible Area") { @@ -176,14 +176,14 @@ } else if (name == "Scale") { - *min = 0; - *max = 2; + *min = 0; + *max = 2; *deflt = 0; - val = (int)m_scale; + val = (int)m_scale; } else { - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -191,15 +191,15 @@ QString WaveformLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { if (name == "Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Meter"); - case 2: return tr("dB"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Meter"); + case 2: return tr("dB"); + } } if (name == "Channels") { switch (value) { @@ -225,7 +225,7 @@ WaveformLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(float(pow(10, float(value)/20.0))); + setGain(float(pow(10, float(value)/20.0))); } else if (name == "Normalize Visible Area") { setAutoNormalize(value ? true : false); } else if (name == "Channels") { @@ -233,12 +233,12 @@ else if (value == 2) setChannelMode(MergeChannels); else setChannelMode(SeparateChannels); } else if (name == "Scale") { - switch (value) { - default: - case 0: setScale(LinearScale); break; - case 1: setScale(MeterScale); break; - case 2: setScale(dBScale); break; - } + switch (value) { + default: + case 0: setScale(LinearScale); break; + case 1: setScale(MeterScale); break; + case 2: setScale(dBScale); break; + } } else { SingleColourLayer::setProperty(name, value); } @@ -378,19 +378,19 @@ int rawChannels = channels; if (m_channel == -1) { - min = 0; - if (m_channelMode == MergeChannels || + min = 0; + if (m_channelMode == MergeChannels || m_channelMode == MixChannels) { - max = 0; - channels = 1; - } else { - max = channels - 1; - } + max = 0; + channels = 1; + } else { + max = channels - 1; + } } else { - min = m_channel; - max = m_channel; - rawChannels = 1; - channels = 1; + min = m_channel; + max = m_channel; + rawChannels = 1; + channels = 1; } merging = (m_channelMode == MergeChannels && rawChannels > 1); @@ -479,7 +479,7 @@ WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const { if (!m_model || !m_model->isOK()) { - return; + return; } int zoomLevel = v->getZoomLevel(); @@ -487,7 +487,7 @@ #ifdef DEBUG_WAVEFORM_PAINT Profiler profiler("WaveformLayer::paint", true); cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y() - << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl; + << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl; #endif int channels = 0, minChannel = 0, maxChannel = 0; @@ -509,37 +509,37 @@ cerr << "WaveformLayer::paint: aggressive is true" << endl; #endif - if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) { - m_cacheValid = false; - } + if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) { + m_cacheValid = false; + } - if (!m_cache || m_cache->width() != w || m_cache->height() != h) { + if (!m_cache || m_cache->width() != w || m_cache->height() != h) { #ifdef DEBUG_WAVEFORM_PAINT if (m_cache) { cerr << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << endl; } #endif - delete m_cache; - m_cache = new QPixmap(w, h); + delete m_cache; + m_cache = new QPixmap(w, h); m_cacheValid = false; - } + } - if (m_cacheValid) { - viewPainter.drawPixmap(rect, *m_cache, rect); - return; - } + if (m_cacheValid) { + viewPainter.drawPixmap(rect, *m_cache, rect); + return; + } - paint = new QPainter(m_cache); + paint = new QPainter(m_cache); - paint->setPen(Qt::NoPen); - paint->setBrush(getBackgroundQColor(v)); - paint->drawRect(rect); + paint->setPen(Qt::NoPen); + paint->setBrush(getBackgroundQColor(v)); + paint->drawRect(rect); - paint->setPen(getForegroundQColor(v)); - paint->setBrush(Qt::NoBrush); + paint->setPen(getForegroundQColor(v)); + paint->setBrush(Qt::NoBrush); } else { - paint = &viewPainter; + paint = &viewPainter; } paint->setRenderHint(QPainter::Antialiasing, false); @@ -597,11 +597,11 @@ QColor midColour = baseColour; if (midColour == Qt::black) { - midColour = Qt::gray; + midColour = Qt::gray; } else if (v->hasLightBackground()) { - midColour = midColour.light(150); + midColour = midColour.light(150); } else { - midColour = midColour.light(50); + midColour = midColour.light(50); } while ((int)m_effectiveGains.size() <= maxChannel) { @@ -610,8 +610,8 @@ for (int ch = minChannel; ch <= maxChannel; ++ch) { - int prevRangeBottom = -1, prevRangeTop = -1; - QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; + int prevRangeBottom = -1, prevRangeTop = -1; + QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; m_effectiveGains[ch] = m_gain; @@ -621,14 +621,14 @@ double gain = m_effectiveGains[ch]; - int m = (h / channels) / 2; - int my = m + (((ch - minChannel) * h) / channels); + int m = (h / channels) / 2; + int my = m + (((ch - minChannel) * h) / channels); -#ifdef DEBUG_WAVEFORM_PAINT - cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl; +#ifdef DEBUG_WAVEFORM_PAINT + cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl; #endif - if (my - m > y1 || my + m < y0) continue; + if (my - m > y1 || my + m < y0) continue; if ((m_scale == dBScale || m_scale == MeterScale) && m_channelMode != MergeChannels) { @@ -636,8 +636,8 @@ my = m + (((ch - minChannel) * h) / channels); } - paint->setPen(greys[1]); - paint->drawLine(x0, my, x1, my); + paint->setPen(greys[1]); + paint->drawLine(x0, my, x1, my); int n = 10; int py = -1; @@ -694,7 +694,7 @@ cerr << "channel " << ch << ": " << ranges->size() << " ranges from " << frame0 << " to " << frame1 << " at zoom level " << modelZoomLevel << endl; #endif - if (mergingChannels || mixingChannels) { + if (mergingChannels || mixingChannels) { if (m_model->getChannelCount() > 1) { if (!otherChannelRanges) { otherChannelRanges = @@ -707,11 +707,11 @@ if (otherChannelRanges != ranges) delete otherChannelRanges; otherChannelRanges = ranges; } - } + } - for (int x = x0; x <= x1; ++x) { + for (int x = x0; x <= x1; ++x) { - range = RangeSummarisableTimeValueModel::Range(); + range = RangeSummarisableTimeValueModel::Range(); sv_frame_t f0, f1; if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue; @@ -733,50 +733,50 @@ cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << endl; } - if (ranges && i0 < (sv_frame_t)ranges->size()) { + if (ranges && i0 < (sv_frame_t)ranges->size()) { - range = (*ranges)[size_t(i0)]; + range = (*ranges)[size_t(i0)]; - if (i1 > i0 && i1 < (int)ranges->size()) { - range.setMax(std::max(range.max(), + if (i1 > i0 && i1 < (int)ranges->size()) { + range.setMax(std::max(range.max(), (*ranges)[size_t(i1)].max())); - range.setMin(std::min(range.min(), + range.setMin(std::min(range.min(), (*ranges)[size_t(i1)].min())); - range.setAbsmean((range.absmean() + range.setAbsmean((range.absmean() + (*ranges)[size_t(i1)].absmean()) / 2); - } + } - } else { + } else { #ifdef DEBUG_WAVEFORM_PAINT cerr << "No (or not enough) ranges for i0 = " << i0 << endl; #endif - continue; - } + continue; + } - int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0; + int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0; - if (mergingChannels) { + if (mergingChannels) { - if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { + if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { - range.setMax(fabsf(range.max())); - range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max())); - range.setAbsmean + range.setMax(fabsf(range.max())); + range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max())); + range.setAbsmean ((range.absmean() + (*otherChannelRanges)[size_t(i0)].absmean()) / 2); - if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) { - // let's not concern ourselves about the mean - range.setMin + if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) { + // let's not concern ourselves about the mean + range.setMin (std::min (range.min(), -fabsf((*otherChannelRanges)[size_t(i1)].max()))); - } - } + } + } - } else if (mixingChannels) { + } else if (mixingChannels) { - if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { + if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { range.setMax((range.max() + (*otherChannelRanges)[size_t(i0)].max()) / 2); @@ -787,19 +787,19 @@ } } - int greyLevels = 1; - if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4; + int greyLevels = 1; + if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4; - switch (m_scale) { + switch (m_scale) { - case LinearScale: - rangeBottom = int(double(m * greyLevels) * range.min() * gain); - rangeTop = int(double(m * greyLevels) * range.max() * gain); - meanBottom = int(double(-m) * range.absmean() * gain); - meanTop = int(double(m) * range.absmean() * gain); - break; + case LinearScale: + rangeBottom = int(double(m * greyLevels) * range.min() * gain); + rangeTop = int(double(m * greyLevels) * range.max() * gain); + meanBottom = int(double(-m) * range.absmean() * gain); + meanTop = int(double(m) * range.absmean() * gain); + break; - case dBScale: + case dBScale: if (!mergingChannels) { int db0 = dBscale(range.min() * gain, m); int db1 = dBscale(range.max() * gain, m); @@ -814,9 +814,9 @@ meanBottom = -dBscale(range.absmean() * gain, m); meanTop = dBscale(range.absmean() * gain, m); } - break; + break; - case MeterScale: + case MeterScale: if (!mergingChannels) { int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m)); int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m)); @@ -832,73 +832,73 @@ meanTop = AudioLevel::multiplier_to_preview(range.absmean() * gain, m); } break; - } + } - rangeBottom = my * greyLevels - rangeBottom; - rangeTop = my * greyLevels - rangeTop; - meanBottom = my - meanBottom; - meanTop = my - meanTop; + rangeBottom = my * greyLevels - rangeBottom; + rangeTop = my * greyLevels - rangeTop; + meanBottom = my - meanBottom; + meanTop = my - meanTop; - int topFill = (rangeTop % greyLevels); - if (topFill > 0) topFill = greyLevels - topFill; + int topFill = (rangeTop % greyLevels); + if (topFill > 0) topFill = greyLevels - topFill; - int bottomFill = (rangeBottom % greyLevels); + int bottomFill = (rangeBottom % greyLevels); - rangeTop = rangeTop / greyLevels; - rangeBottom = rangeBottom / greyLevels; + rangeTop = rangeTop / greyLevels; + rangeBottom = rangeBottom / greyLevels; - bool clipped = false; + bool clipped = false; - if (rangeTop < my - m) { rangeTop = my - m; } - if (rangeTop > my + m) { rangeTop = my + m; } - if (rangeBottom < my - m) { rangeBottom = my - m; } - if (rangeBottom > my + m) { rangeBottom = my + m; } + if (rangeTop < my - m) { rangeTop = my - m; } + if (rangeTop > my + m) { rangeTop = my + m; } + if (rangeBottom < my - m) { rangeBottom = my - m; } + if (rangeBottom > my + m) { rangeBottom = my + m; } - if (range.max() <= -1.0 || - range.max() >= 1.0) clipped = true; - - if (meanBottom > rangeBottom) meanBottom = rangeBottom; - if (meanTop < rangeTop) meanTop = rangeTop; + if (range.max() <= -1.0 || + range.max() >= 1.0) clipped = true; + + if (meanBottom > rangeBottom) meanBottom = rangeBottom; + if (meanTop < rangeTop) meanTop = rangeTop; - bool drawMean = m_showMeans; - if (meanTop == rangeTop) { - if (meanTop < meanBottom) ++meanTop; - else drawMean = false; - } - if (meanBottom == rangeBottom && m_scale == LinearScale) { - if (meanBottom > meanTop) --meanBottom; - else drawMean = false; - } + bool drawMean = m_showMeans; + if (meanTop == rangeTop) { + if (meanTop < meanBottom) ++meanTop; + else drawMean = false; + } + if (meanBottom == rangeBottom && m_scale == LinearScale) { + if (meanBottom > meanTop) --meanBottom; + else drawMean = false; + } - if (x != x0 && prevRangeBottom != -1) { - if (prevRangeBottom > rangeBottom + 1 && - prevRangeTop > rangeBottom + 1) { -// paint->setPen(midColour); - paint->setPen(baseColour); - paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1); - paint->setPen(prevRangeTopColour); - paint->drawPoint(x-1, prevRangeTop); - } else if (prevRangeBottom < rangeTop - 1 && - prevRangeTop < rangeTop - 1) { -// paint->setPen(midColour); - paint->setPen(baseColour); - paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1); - paint->setPen(prevRangeBottomColour); - paint->drawPoint(x-1, prevRangeBottom); - } - } + if (x != x0 && prevRangeBottom != -1) { + if (prevRangeBottom > rangeBottom + 1 && + prevRangeTop > rangeBottom + 1) { +// paint->setPen(midColour); + paint->setPen(baseColour); + paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1); + paint->setPen(prevRangeTopColour); + paint->drawPoint(x-1, prevRangeTop); + } else if (prevRangeBottom < rangeTop - 1 && + prevRangeTop < rangeTop - 1) { +// paint->setPen(midColour); + paint->setPen(baseColour); + paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1); + paint->setPen(prevRangeBottomColour); + paint->drawPoint(x-1, prevRangeBottom); + } + } - if (ready) { - if (clipped /*!!! || - range.min() * gain <= -1.0 || - range.max() * gain >= 1.0 */) { - paint->setPen(Qt::red); //!!! getContrastingColour - } else { - paint->setPen(baseColour); - } - } else { - paint->setPen(midColour); - } + if (ready) { + if (clipped /*!!! || + range.min() * gain <= -1.0 || + range.max() * gain >= 1.0 */) { + paint->setPen(Qt::red); //!!! getContrastingColour + } else { + paint->setPen(baseColour); + } + } else { + paint->setPen(midColour); + } #ifdef DEBUG_WAVEFORM_PAINT cerr << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl; @@ -910,36 +910,36 @@ paint->drawLine(x, rangeBottom, x, rangeTop); } - prevRangeTopColour = baseColour; - prevRangeBottomColour = baseColour; + prevRangeTopColour = baseColour; + prevRangeBottomColour = baseColour; - if (m_greyscale && (m_scale == LinearScale) && ready) { - if (!clipped) { - if (rangeTop < rangeBottom) { - if (topFill > 0 && - (!drawMean || (rangeTop < meanTop - 1))) { - paint->setPen(greys[topFill - 1]); - paint->drawPoint(x, rangeTop); - prevRangeTopColour = greys[topFill - 1]; - } - if (bottomFill > 0 && - (!drawMean || (rangeBottom > meanBottom + 1))) { - paint->setPen(greys[bottomFill - 1]); - paint->drawPoint(x, rangeBottom); - prevRangeBottomColour = greys[bottomFill - 1]; - } - } - } - } + if (m_greyscale && (m_scale == LinearScale) && ready) { + if (!clipped) { + if (rangeTop < rangeBottom) { + if (topFill > 0 && + (!drawMean || (rangeTop < meanTop - 1))) { + paint->setPen(greys[topFill - 1]); + paint->drawPoint(x, rangeTop); + prevRangeTopColour = greys[topFill - 1]; + } + if (bottomFill > 0 && + (!drawMean || (rangeBottom > meanBottom + 1))) { + paint->setPen(greys[bottomFill - 1]); + paint->drawPoint(x, rangeBottom); + prevRangeBottomColour = greys[bottomFill - 1]; + } + } + } + } - if (drawMean) { - paint->setPen(midColour); - paint->drawLine(x, meanBottom, x, meanTop); - } + if (drawMean) { + paint->setPen(midColour); + paint->drawLine(x, meanBottom, x, meanTop); + } - prevRangeBottom = rangeBottom; - prevRangeTop = rangeTop; - } + prevRangeBottom = rangeBottom; + prevRangeTop = rangeTop; + } } if (m_middleLineHeight != 0.5) { @@ -948,13 +948,13 @@ if (m_aggressive) { - if (ready && rect == v->getPaintRect()) { - m_cacheValid = true; - m_cacheZoomLevel = zoomLevel; - } - paint->end(); - delete paint; - viewPainter.drawPixmap(rect, *m_cache, rect); + if (ready && rect == v->getPaintRect()) { + m_cacheValid = true; + m_cacheZoomLevel = zoomLevel; + } + paint->end(); + delete paint; + viewPainter.drawPixmap(rect, *m_cache, rect); } if (otherChannelRanges != ranges) delete otherChannelRanges; @@ -981,12 +981,12 @@ RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate()); if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) { - text += tr("Time:\t%1 - %2") - .arg(rt0.toText(true).c_str()) - .arg(rt1.toText(true).c_str()); + text += tr("Time:\t%1 - %2") + .arg(rt0.toText(true).c_str()) + .arg(rt1.toText(true).c_str()); } else { - text += tr("Time:\t%1") - .arg(rt0.toText(true).c_str()); + text += tr("Time:\t%1") + .arg(rt0.toText(true).c_str()); } int channels = 0, minChannel = 0, maxChannel = 0; @@ -998,20 +998,20 @@ for (int ch = minChannel; ch <= maxChannel; ++ch) { - int blockSize = v->getZoomLevel(); - RangeSummarisableTimeValueModel::RangeBlock ranges; + int blockSize = v->getZoomLevel(); + RangeSummarisableTimeValueModel::RangeBlock ranges; m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize); - if (ranges.empty()) continue; - - RangeSummarisableTimeValueModel::Range range = ranges[0]; - - QString label = tr("Level:"); - if (minChannel != maxChannel) { - if (ch == 0) label = tr("Left:"); - else if (ch == 1) label = tr("Right:"); - else label = tr("Channel %1").arg(ch + 1); - } + if (ranges.empty()) continue; + + RangeSummarisableTimeValueModel::Range range = ranges[0]; + + QString label = tr("Level:"); + if (minChannel != maxChannel) { + if (ch == 0) label = tr("Left:"); + else if (ch == 1) label = tr("Right:"); + else label = tr("Channel %1").arg(ch + 1); + } bool singleValue = false; double min, max; @@ -1028,17 +1028,17 @@ max = double(imax)/10000; } - int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()), - fabsf(range.max()))) - * 100); + int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()), + fabsf(range.max()))) + * 100); - if (!singleValue) { - text += tr("\n%1\t%2 - %3 (%4 dB peak)") - .arg(label).arg(min).arg(max).arg(double(db)/100); - } else { - text += tr("\n%1\t%2 (%3 dB peak)") - .arg(label).arg(min).arg(double(db)/100); - } + if (!singleValue) { + text += tr("\n%1\t%2 - %3 (%4 dB peak)") + .arg(label).arg(min).arg(max).arg(double(db)/100); + } else { + text += tr("\n%1\t%2 (%3 dB peak)") + .arg(label).arg(min).arg(double(db)/100); + } } return text; @@ -1057,7 +1057,7 @@ int h = v->getPaintHeight(); int m = (h / channels) / 2; - + if ((m_scale == dBScale || m_scale == MeterScale) && m_channelMode != MergeChannels) { m = (h / channels); @@ -1201,10 +1201,10 @@ WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { if (m_scale == LinearScale) { - return paint.fontMetrics().width("0.0") + 13; + return paint.fontMetrics().width("0.0") + 13; } else { - return std::max(paint.fontMetrics().width(tr("0dB")), - paint.fontMetrics().width(Strings::minus_infinity)) + 13; + return std::max(paint.fontMetrics().width(tr("0dB")), + paint.fontMetrics().width(Strings::minus_infinity)) + 13; } } @@ -1212,7 +1212,7 @@ WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) { - return; + return; } int channels = 0, minChannel = 0, maxChannel = 0; @@ -1230,23 +1230,23 @@ for (int ch = minChannel; ch <= maxChannel; ++ch) { - int lastLabelledY = -1; + int lastLabelledY = -1; if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch]; int n = 10; - for (int i = 0; i <= n; ++i) { + for (int i = 0; i <= n; ++i) { double val = 0.0, nval = 0.0; - QString text = ""; + QString text = ""; switch (m_scale) { case LinearScale: val = (i * gain) / n; - text = QString("%1").arg(double(i) / n); - if (i == 0) text = "0.0"; + text = QString("%1").arg(double(i) / n); + if (i == 0) text = "0.0"; else { nval = -val; if (i == n) text = "1.0"; @@ -1255,22 +1255,22 @@ case MeterScale: val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain; - text = QString("%1").arg(meterdbs[i]); - if (i == n) text = tr("0dB"); - if (i == 0) { + text = QString("%1").arg(meterdbs[i]); + if (i == n) text = tr("0dB"); + if (i == 0) { text = Strings::minus_infinity; val = 0.0; - } + } break; case dBScale: val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain; - text = QString("%1").arg(-(10*n) + i * 10); - if (i == n) text = tr("0dB"); - if (i == 0) { + text = QString("%1").arg(-(10*n) + i * 10); + if (i == n) text = tr("0dB"); + if (i == 0) { text = Strings::minus_infinity; val = 0.0; - } + } break; } @@ -1325,7 +1325,7 @@ paint.drawLine(w - 4, y, w, y); if (ny != y) paint.drawLine(w - 4, ny, w, ny); } - } + } } } @@ -1340,22 +1340,22 @@ (m_colour, colourName, colourSpec, darkbg); s += QString("gain=\"%1\" " - "showMeans=\"%2\" " - "greyscale=\"%3\" " - "channelMode=\"%4\" " - "channel=\"%5\" " + "showMeans=\"%2\" " + "greyscale=\"%3\" " + "channelMode=\"%4\" " + "channel=\"%5\" " "scale=\"%6\" " "middleLineHeight=\"%7\" " - "aggressive=\"%8\" " + "aggressive=\"%8\" " "autoNormalize=\"%9\"") - .arg(m_gain) - .arg(m_showMeans) - .arg(m_greyscale) - .arg(m_channelMode) - .arg(m_channel) - .arg(m_scale) + .arg(m_gain) + .arg(m_showMeans) + .arg(m_greyscale) + .arg(m_channelMode) + .arg(m_channel) + .arg(m_scale) .arg(m_middleLineHeight) - .arg(m_aggressive) + .arg(m_aggressive) .arg(m_autoNormalize); SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s); @@ -1372,15 +1372,15 @@ if (ok) setGain(gain); bool showMeans = (attributes.value("showMeans") == "1" || - attributes.value("showMeans") == "true"); + attributes.value("showMeans") == "true"); setShowMeans(showMeans); bool greyscale = (attributes.value("greyscale") == "1" || - attributes.value("greyscale") == "true"); + attributes.value("greyscale") == "true"); setUseGreyscale(greyscale); ChannelMode channelMode = (ChannelMode) - attributes.value("channelMode").toInt(&ok); + attributes.value("channelMode").toInt(&ok); if (ok) setChannelMode(channelMode); int channel = attributes.value("channel").toInt(&ok); @@ -1393,7 +1393,7 @@ if (ok) setMiddleLineHeight(middleLineHeight); bool aggressive = (attributes.value("aggressive") == "1" || - attributes.value("aggressive") == "true"); + attributes.value("aggressive") == "true"); setUseGreyscale(aggressive); bool autoNormalize = (attributes.value("autoNormalize") == "1" || diff -r 57d192e26331 -r 13d9b422f7fe layer/WaveformLayer.h --- a/layer/WaveformLayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/WaveformLayer.h Mon Sep 17 13:51:31 2018 +0100 @@ -59,7 +59,7 @@ virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; virtual void setProperty(const PropertyName &, int value); diff -r 57d192e26331 -r 13d9b422f7fe view/AlignmentView.cpp --- a/view/AlignmentView.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/AlignmentView.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -43,10 +43,10 @@ { View::viewCentreFrameChanged(v, f); if (v == m_above) { - m_centreFrame = f; - update(); + m_centreFrame = f; + update(); } else if (v == m_below) { - update(); + update(); } } @@ -73,15 +73,15 @@ AlignmentView::setViewAbove(View *v) { if (m_above) { - disconnect(m_above, 0, this, 0); + disconnect(m_above, 0, this, 0); } m_above = v; if (m_above) { - connect(m_above, + connect(m_above, SIGNAL(zoomLevelChanged(ZoomLevel, bool)), - this, + this, SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool))); } } @@ -90,15 +90,15 @@ AlignmentView::setViewBelow(View *v) { if (m_below) { - disconnect(m_below, 0, this, 0); + disconnect(m_below, 0, this, 0); } m_below = v; if (m_below) { - connect(m_below, + connect(m_below, SIGNAL(zoomLevelChanged(ZoomLevel, bool)), - this, + this, SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool))); } } @@ -130,11 +130,11 @@ vector keyFrames = getKeyFrames(); foreach (sv_frame_t f, keyFrames) { - int ax = m_above->getXForFrame(f); - sv_frame_t rf = m_above->alignToReference(f); - sv_frame_t bf = m_below->alignFromReference(rf); - int bx = m_below->getXForFrame(bf); - paint.drawLine(ax, 0, bx, height()); + int ax = m_above->getXForFrame(f); + sv_frame_t rf = m_above->alignToReference(f); + sv_frame_t bf = m_below->alignFromReference(rf); + int bx = m_below->getXForFrame(bf); + paint.drawLine(ax, 0, bx, height()); } paint.end(); @@ -144,31 +144,31 @@ AlignmentView::getKeyFrames() { if (!m_above) { - return getDefaultKeyFrames(); + return getDefaultKeyFrames(); } SparseOneDimensionalModel *m = 0; // get the topmost such for (int i = 0; i < m_above->getLayerCount(); ++i) { - if (qobject_cast(m_above->getLayer(i))) { - SparseOneDimensionalModel *mm = - qobject_cast - (m_above->getLayer(i)->getModel()); - if (mm) m = mm; - } + if (qobject_cast(m_above->getLayer(i))) { + SparseOneDimensionalModel *mm = + qobject_cast + (m_above->getLayer(i)->getModel()); + if (mm) m = mm; + } } if (!m) { - return getDefaultKeyFrames(); + return getDefaultKeyFrames(); } 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); + pi != pp.end(); ++pi) { + keyFrames.push_back(pi->frame); } return keyFrames; @@ -185,9 +185,9 @@ if (rate == 0) return keyFrames; for (sv_frame_t f = m_above->getModelsStartFrame(); - f <= m_above->getModelsEndFrame(); - f += sv_frame_t(rate * 5 + 0.5)) { - keyFrames.push_back(f); + f <= m_above->getModelsEndFrame(); + f += sv_frame_t(rate * 5 + 0.5)) { + keyFrames.push_back(f); } return keyFrames; diff -r 57d192e26331 -r 13d9b422f7fe view/Overview.cpp --- a/view/Overview.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/Overview.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -117,7 +117,7 @@ cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl; #endif if (m_views.find(v) != m_views.end()) { - update(); + update(); } } @@ -126,7 +126,7 @@ { if (v == this) return; if (m_views.find(v) != m_views.end()) { - update(); + update(); } } @@ -181,26 +181,26 @@ int zoomLevel = int(frameCount / width()); if (zoomLevel < 1) zoomLevel = 1; zoomLevel = getZoomConstraintBlockSize(zoomLevel, - ZoomConstraint::RoundUp); + ZoomConstraint::RoundUp); if (zoomLevel != m_zoomLevel) { - m_zoomLevel = zoomLevel; - emit zoomLevelChanged(m_zoomLevel, m_followZoom); + m_zoomLevel = zoomLevel; + emit zoomLevelChanged(m_zoomLevel, m_followZoom); } sv_frame_t centreFrame = startFrame + m_zoomLevel * (width() / 2); if (centreFrame > (startFrame + getModelsEndFrame())/2) { - centreFrame = (startFrame + getModelsEndFrame())/2; + centreFrame = (startFrame + getModelsEndFrame())/2; } if (centreFrame != m_centreFrame) { #ifdef DEBUG_OVERVIEW cerr << "Overview::paintEvent: Centre frame changed from " << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame(); #endif - m_centreFrame = centreFrame; + m_centreFrame = centreFrame; #ifdef DEBUG_OVERVIEW cerr << " to " << getStartFrame() << endl; #endif - emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore); + emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore); } View::paintEvent(e); @@ -210,8 +210,6 @@ paint.setClipRegion(e->region()); paint.setRenderHints(QPainter::Antialiasing); - QRect r(rect()); - // We paint a rounded rect for each distinct set of view extents, // and we colour in the inside and outside of the rect that // corresponds to the current view. (One small caveat -- we don't @@ -225,12 +223,12 @@ int y = 0; for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { - if (!*i) continue; + if (!*i) continue; - View *w = (View *)*i; + View *w = (View *)*i; - sv_frame_t f0 = w->getFrameForX(0); - sv_frame_t f1 = w->getFrameForX(w->width()); + sv_frame_t f0 = w->getFrameForX(0); + sv_frame_t f1 = w->getFrameForX(w->width()); if (f0 >= 0) { sv_frame_t rf0 = w->alignToReference(f0); @@ -241,17 +239,16 @@ f1 = alignFromReference(rf1); } - int x0 = getXForFrame(f0); - int x1 = getXForFrame(f1); + int x0 = getXForFrame(f0); + int x1 = getXForFrame(f1); - - if (x1 <= x0) x1 = x0 + 1; + if (x1 <= x0) x1 = x0 + 1; std::pair extent(x0, x1); if (extents.find(extent) == extents.end()) { - y += height() / 10 + 1; + y += height() / 10 + 1; extents.insert(extent); QRect vr(x0, y, x1 - x0, height() - 2 * y); @@ -289,7 +286,7 @@ m_clickedInRange = true; for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { - if (*i && (*i)->getAligningModel() == getAligningModel()) { + if (*i && (*i)->getAligningModel() == getAligningModel()) { m_dragCentreFrame = (*i)->getCentreFrame(); break; } @@ -300,7 +297,7 @@ Overview::mouseReleaseEvent(QMouseEvent *e) { if (m_clickedInRange) { - mouseMoveEvent(e); + mouseMoveEvent(e); } m_clickedInRange = false; } @@ -315,20 +312,20 @@ sv_frame_t newCentreFrame = m_dragCentreFrame; if (frameOff > 0) { - newCentreFrame += frameOff; + newCentreFrame += frameOff; } else if (newCentreFrame >= -frameOff) { - newCentreFrame += frameOff; + newCentreFrame += frameOff; } else { - newCentreFrame = 0; + newCentreFrame = 0; } if (newCentreFrame >= getModelsEndFrame()) { - newCentreFrame = getModelsEndFrame(); - if (newCentreFrame > 0) --newCentreFrame; + newCentreFrame = getModelsEndFrame(); + if (newCentreFrame > 0) --newCentreFrame; } if (std::max(m_centreFrame, newCentreFrame) - - std::min(m_centreFrame, newCentreFrame) > m_zoomLevel) { + std::min(m_centreFrame, newCentreFrame) > m_zoomLevel) { sv_frame_t rf = alignToReference(newCentreFrame); #ifdef DEBUG_OVERVIEW cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl; diff -r 57d192e26331 -r 13d9b422f7fe view/Pane.cpp --- a/view/Pane.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/Pane.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -22,6 +22,7 @@ #include "ViewManager.h" #include "widgets/CommandHistory.h" #include "widgets/TextAbbrev.h" +#include "widgets/IconLoader.h" #include "base/Preferences.h" #include "layer/WaveformLayer.h" #include "layer/TimeRulerLayer.h" @@ -142,8 +143,8 @@ m_hthumb->setObjectName(tr("Horizontal Zoom")); m_hthumb->setCursor(Qt::ArrowCursor); layout->addWidget(m_hthumb, 1, 0, 1, 2); - m_hthumb->setFixedWidth(70); - m_hthumb->setFixedHeight(16); + m_hthumb->setFixedWidth(m_manager->scalePixelSize(70)); + m_hthumb->setFixedHeight(m_manager->scalePixelSize(16)); m_hthumb->setDefaultValue(0); m_hthumb->setSpeed(0.6f); connect(m_hthumb, SIGNAL(valueChanged(int)), this, @@ -154,8 +155,8 @@ m_vpan = new Panner; m_vpan->setCursor(Qt::ArrowCursor); layout->addWidget(m_vpan, 0, 1); - m_vpan->setFixedWidth(12); - m_vpan->setFixedHeight(70); + m_vpan->setFixedWidth(m_manager->scalePixelSize(12)); + m_vpan->setFixedHeight(m_manager->scalePixelSize(70)); m_vpan->setAlpha(80, 130); connect(m_vpan, SIGNAL(rectExtentsChanged(float, float, float, float)), this, SLOT(verticalPannerMoved(float, float, float, float))); @@ -168,8 +169,8 @@ m_vthumb->setObjectName(tr("Vertical Zoom")); m_vthumb->setCursor(Qt::ArrowCursor); layout->addWidget(m_vthumb, 0, 2); - m_vthumb->setFixedWidth(16); - m_vthumb->setFixedHeight(70); + m_vthumb->setFixedWidth(m_manager->scalePixelSize(16)); + m_vthumb->setFixedHeight(m_manager->scalePixelSize(70)); connect(m_vthumb, SIGNAL(valueChanged(int)), this, SLOT(verticalThumbwheelMoved(int))); connect(m_vthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); @@ -183,9 +184,9 @@ m_reset = new NotifyingPushButton; m_reset->setFlat(true); m_reset->setCursor(Qt::ArrowCursor); - m_reset->setFixedHeight(16); - m_reset->setFixedWidth(16); - m_reset->setIcon(QPixmap(":/icons/zoom-reset.png")); + m_reset->setFixedHeight(m_manager->scalePixelSize(16)); + m_reset->setFixedWidth(m_manager->scalePixelSize(16)); + m_reset->setIcon(IconLoader().load("zoom-reset")); m_reset->setToolTip(tr("Reset zoom to default")); layout->addWidget(m_reset, 1, 2); @@ -284,16 +285,19 @@ updateVerticalPanner(); if (m_manager && m_manager->getZoomWheelsEnabled() && - width() > 120 && height() > 100) { + width() > m_manager->scalePixelSize(120) && + height() > m_manager->scalePixelSize(100)) { if (!m_headsUpDisplay->isVisible()) { m_headsUpDisplay->show(); } + int shift = m_manager->scalePixelSize(86); if (haveVThumb) { m_headsUpDisplay->setFixedHeight(m_vthumb->height() + m_hthumb->height()); - m_headsUpDisplay->move(width() - 86, height() - 86); + m_headsUpDisplay->move(width() - shift, height() - shift); } else { m_headsUpDisplay->setFixedHeight(m_hthumb->height()); - m_headsUpDisplay->move(width() - 86, height() - 16); + m_headsUpDisplay->move(width() - shift, + height() - m_manager->scalePixelSize(16)); } } else { m_headsUpDisplay->hide(); @@ -424,25 +428,8 @@ ViewManager::ToolMode toolMode = ViewManager::NavigateMode; if (m_manager) toolMode = m_manager->getToolModeFor(this); - if (m_manager && - m_mouseInWidget && - toolMode == ViewManager::MeasureMode) { - - for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) { - --vi; - - std::vector crosshairExtents; - - if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint, - crosshairExtents)) { - (*vi)->paintCrosshairs(this, paint, m_identifyPoint); - break; - } else if ((*vi)->isLayerOpaque()) { - break; - } - } - } - + // Locate some relevant layers and models + Layer *topLayer = getTopLayer(); bool haveSomeTimeXAxis = false; @@ -469,16 +456,43 @@ if (waveformModel && workModel && haveSomeTimeXAxis) break; } - m_scaleWidth = 0; - + // Block off left and right extents so we can see where the main model ends + if (workModel && hasTopLayerTimeXAxis()) { drawModelTimeExtents(r, paint, workModel); } + // Crosshairs for mouse movement in measure mode + + if (m_manager && + m_mouseInWidget && + toolMode == ViewManager::MeasureMode) { + + for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) { + --vi; + + std::vector crosshairExtents; + + if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint, + crosshairExtents)) { + (*vi)->paintCrosshairs(this, paint, m_identifyPoint); + break; + } else if ((*vi)->isLayerOpaque()) { + break; + } + } + } + + // Scale width will be set implicitly during drawVerticalScale call + m_scaleWidth = 0; + if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) { drawVerticalScale(r, topLayer, paint); } + // Feature description: the box in top-right showing values from + // the nearest feature to the mouse + if (m_identifyFeatures && m_manager && m_manager->shouldIlluminateLocalFeatures() && topLayer) { @@ -523,6 +537,9 @@ drawLayerNames(r, paint); } + // The blue box that is shown when you ctrl-click in navigate mode + // to define a zoom region + if (m_shiftPressed && m_clickedInRange && (toolMode == ViewManager::NavigateMode || m_navigating)) { @@ -562,6 +579,8 @@ { Layer *scaleLayer = 0; +// cerr << "Pane::drawVerticalScale[" << this << "]" << endl; + double min, max; bool log; QString unit; @@ -575,7 +594,7 @@ int sw = topLayer->getVerticalScaleWidth (this, m_manager->shouldShowVerticalColourScale(), paint); - + if (sw > 0) { scaleLayer = topLayer; m_scaleWidth = sw; @@ -641,7 +660,9 @@ } if (!scaleLayer) m_scaleWidth = 0; - + +// cerr << "m_scaleWidth = " << m_scaleWidth << ", r.left = " << r.left() << endl; + if (m_scaleWidth > 0 && r.left() < m_scaleWidth) { // Profiler profiler("Pane::paintEvent - painting vertical scale", true); @@ -649,10 +670,13 @@ // SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl; paint.save(); + paint.setPen(Qt::NoPen); + paint.setBrush(getBackground()); + paint.drawRect(0, 0, m_scaleWidth, height()); + paint.setPen(getForeground()); - paint.setBrush(getBackground()); - paint.drawRect(0, -1, m_scaleWidth, height()+1); - + paint.drawLine(m_scaleWidth, 0, m_scaleWidth, height()); + paint.setBrush(Qt::NoBrush); scaleLayer->paintVerticalScale (this, m_manager->shouldShowVerticalColourScale(), @@ -730,7 +754,7 @@ c = QColor(240, 240, 240); } - paint.setPen(c); + paint.setPen(PaintAssistant::scalePen(c)); int x = width() / 2; if (!omitLine) { @@ -800,10 +824,10 @@ QBrush brush; if (hasLightBackground()) { - brush = QBrush(QColor("#f8f8f8")); + brush = QBrush(QColor("#aaf8f8f8")); paint.setPen(Qt::black); } else { - brush = QBrush(QColor("#101010")); + brush = QBrush(QColor("#aa101010")); paint.setPen(Qt::white); } @@ -920,7 +944,7 @@ int lly = height() - 6; if (m_manager->getZoomWheelsEnabled()) { - lly -= 20; + lly -= m_manager->scalePixelSize(20); } if (r.y() + r.height() < lly - int(m_layerStack.size()) * fontHeight) { @@ -942,7 +966,7 @@ int llx = width() - maxTextWidth - 5; if (m_manager->getZoomWheelsEnabled()) { - llx -= 36; + llx -= m_manager->scalePixelSize(36); } if (r.x() + r.width() >= llx - fontAscent - 3) { @@ -1119,7 +1143,7 @@ } QImage * -Pane::toNewImage(sv_frame_t f0, sv_frame_t f1) +Pane::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1) { int x0 = int(f0 / getZoomLevel()); int x1 = int(f1 / getZoomLevel()); @@ -1158,9 +1182,9 @@ } QSize -Pane::getImageSize(sv_frame_t f0, sv_frame_t f1) +Pane::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1) { - QSize s = View::getImageSize(f0, f1); + QSize s = View::getRenderedPartImageSize(f0, f1); QImage *image = new QImage(100, 100, QImage::Format_RGB32); QPainter paint(image); @@ -1192,7 +1216,7 @@ if (!m_manager) return Selection(); - sv_frame_t testFrame = getFrameForX(x - 5); + sv_frame_t testFrame = getFrameForX(x - ViewManager::scalePixelSize(5)); if (testFrame < 0) { testFrame = getFrameForX(x); if (testFrame < 0) return Selection(); @@ -1204,13 +1228,15 @@ int lx = getXForFrame(selection.getStartFrame()); int rx = getXForFrame(selection.getEndFrame()); - int fuzz = 2; + int fuzz = ViewManager::scalePixelSize(2); if (x < lx - fuzz || x > rx + fuzz) return Selection(); int width = rx - lx; - fuzz = 3; + fuzz = ViewManager::scalePixelSize(3); if (width < 12) fuzz = width / 4; - if (fuzz < 1) fuzz = 1; + if (fuzz < ViewManager::scalePixelSize(1)) { + fuzz = ViewManager::scalePixelSize(1); + } if (x < lx + fuzz) closeToLeftEdge = true; if (x > rx - fuzz) closeToRightEdge = true; @@ -1869,11 +1895,11 @@ } double ratio = double(w) / double(width()); -// cerr << "ratio: " << ratio << endl; +// cerr << "ratio: " << ratio << endl; int newZoomLevel = (int)nearbyint(m_zoomLevel * ratio); if (newZoomLevel < 1) newZoomLevel = 1; -// cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << endl; +// cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << endl; setZoomLevel(getZoomConstraintBlockSize(newZoomLevel)); setStartFrame(newStartFrame); @@ -2093,8 +2119,8 @@ layer->snapToFeatureFrame(this, snapFrameRight, resolution, Layer::SnapRight); } - -// cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << endl; + +// cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << endl; if (snapFrameLeft < 0) snapFrameLeft = 0; if (snapFrameRight < 0) snapFrameRight = 0; @@ -2265,7 +2291,7 @@ void Pane::wheelEvent(QWheelEvent *e) { - cerr << "wheelEvent, delta " << e->delta() << ", angleDelta " << e->angleDelta().x() << "," << e->angleDelta().y() << ", pixelDelta " << e->pixelDelta().x() << "," << e->pixelDelta().y() << ", modifiers " << e->modifiers() << endl; +// cerr << "wheelEvent, delta " << e->delta() << ", angleDelta " << e->angleDelta().x() << "," << e->angleDelta().y() << ", pixelDelta " << e->pixelDelta().x() << "," << e->pixelDelta().y() << ", modifiers " << e->modifiers() << endl; e->accept(); // we never want wheel events on the pane to be propagated @@ -2288,7 +2314,7 @@ } if (e->phase() == Qt::ScrollBegin || - fabs(d) >= 120 || + std::abs(d) >= 120 || (d > 0 && m_pendingWheelAngle < 0) || (d < 0 && m_pendingWheelAngle > 0)) { m_pendingWheelAngle = d; @@ -2334,7 +2360,7 @@ void Pane::wheelVertical(int sign, Qt::KeyboardModifiers mods) { - cerr << "wheelVertical: sign = " << sign << endl; +// cerr << "wheelVertical: sign = " << sign << endl; if (mods & Qt::ShiftModifier) { @@ -2381,7 +2407,7 @@ void Pane::wheelHorizontal(int sign, Qt::KeyboardModifiers mods) { - cerr << "wheelHorizontal: sign = " << sign << endl; +// cerr << "wheelHorizontal: sign = " << sign << endl; // Scroll left or right, rapidly @@ -2391,7 +2417,7 @@ void Pane::wheelHorizontalFine(int pixels, Qt::KeyboardModifiers) { - cerr << "wheelHorizontalFine: pixels = " << pixels << endl; +// cerr << "wheelHorizontalFine: pixels = " << pixels << endl; // Scroll left or right by a fixed number of pixels diff -r 57d192e26331 -r 13d9b422f7fe view/Pane.h --- a/view/Pane.h Mon Dec 12 15:18:52 2016 +0000 +++ b/view/Pane.h Mon Sep 17 13:51:31 2018 +0100 @@ -37,28 +37,35 @@ public: Pane(QWidget *parent = 0); - virtual QString getPropertyContainerIconName() const { return "pane"; } + virtual QString getPropertyContainerIconName() const override { return "pane"; } virtual bool shouldIlluminateLocalFeatures(const Layer *layer, - QPoint &pos) const; + QPoint &pos) const override; virtual bool shouldIlluminateLocalSelection(QPoint &pos, - bool &closeToLeft, - bool &closeToRight) const; + bool &closeToLeft, + bool &closeToRight) const override; void setCentreLineVisible(bool visible); bool getCentreLineVisible() const { return m_centreLineVisible; } - virtual sv_frame_t getFirstVisibleFrame() const; + virtual sv_frame_t getFirstVisibleFrame() const override; - virtual int getVerticalScaleWidth() const; + int getVerticalScaleWidth() const; - virtual QImage *toNewImage(sv_frame_t f0, sv_frame_t f1); - virtual QImage *toNewImage() { return View::toNewImage(); } - virtual QSize getImageSize(sv_frame_t f0, sv_frame_t f1); - virtual QSize getImageSize() { return View::getImageSize(); } + virtual QImage *renderToNewImage() override { + return View::renderToNewImage(); + } + + virtual QImage *renderPartToNewImage(sv_frame_t f0, sv_frame_t f1) override; + + virtual QSize getRenderedImageSize() override { + return View::getRenderedImageSize(); + } + + virtual QSize getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1) override; virtual void toXml(QTextStream &stream, QString indent = "", - QString extraAttributes = "") const; + QString extraAttributes = "") const override; static void registerShortcuts(KeyReference &kr); @@ -77,20 +84,22 @@ void regionOutlined(QRect rect); public slots: - virtual void toolModeChanged(); - virtual void zoomWheelsEnabledChanged(); - virtual void viewZoomLevelChanged(View *v, int z, bool locked); - virtual void modelAlignmentCompletionChanged(); + // view slots + virtual void toolModeChanged() override; + virtual void zoomWheelsEnabledChanged() override; + virtual void viewZoomLevelChanged(View *v, int z, bool locked) override; + virtual void modelAlignmentCompletionChanged() override; + // local slots, not overrides virtual void horizontalThumbwheelMoved(int value); virtual void verticalThumbwheelMoved(int value); virtual void verticalZoomChanged(); virtual void verticalPannerMoved(float x, float y, float w, float h); virtual void editVerticalPannerExtents(); - virtual void layerParametersChanged(); + virtual void layerParametersChanged() override; - virtual void propertyContainerSelected(View *, PropertyContainer *pc); + virtual void propertyContainerSelected(View *, PropertyContainer *pc) override; void zoomToRegion(QRect r); @@ -101,17 +110,17 @@ void playbackScheduleTimerElapsed(); protected: - virtual void paintEvent(QPaintEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mouseDoubleClickEvent(QMouseEvent *e); - virtual void enterEvent(QEvent *e); - virtual void leaveEvent(QEvent *e); - virtual void wheelEvent(QWheelEvent *e); - virtual void resizeEvent(QResizeEvent *e); - virtual void dragEnterEvent(QDragEnterEvent *e); - virtual void dropEvent(QDropEvent *e); + virtual void paintEvent(QPaintEvent *e) override; + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseDoubleClickEvent(QMouseEvent *e) override; + virtual void enterEvent(QEvent *e) override; + virtual void leaveEvent(QEvent *e) override; + virtual void wheelEvent(QWheelEvent *e) override; + virtual void resizeEvent(QResizeEvent *e) override; + virtual void dragEnterEvent(QDragEnterEvent *e) override; + virtual void dropEvent(QDropEvent *e) override; void wheelVertical(int sign, Qt::KeyboardModifiers); void wheelHorizontal(int sign, Qt::KeyboardModifiers); @@ -127,7 +136,7 @@ void drawEditingSelection(QPainter &); void drawAlignmentStatus(QRect, QPainter &, const Model *, bool down); - virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1); + virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1) override; Selection getSelectionAt(int x, bool &closeToLeft, bool &closeToRight) const; diff -r 57d192e26331 -r 13d9b422f7fe view/PaneStack.cpp --- a/view/PaneStack.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/PaneStack.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -131,11 +131,11 @@ QWidget *properties = 0; if (suppressPropertyBox) { - properties = new QFrame(); + properties = new QFrame(); } else { - properties = new PropertyStack(frame, pane); - connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)), - this, SLOT(propertyContainerSelected(View *, PropertyContainer *))); + properties = new PropertyStack(frame, pane); + connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)), + this, SLOT(propertyContainerSelected(View *, PropertyContainer *))); connect(properties, SIGNAL(viewSelected(View *)), this, SLOT(viewSelected(View *))); connect(properties, SIGNAL(contextHelpChanged(const QString &)), @@ -163,11 +163,11 @@ m_splitter->insertWidget(index, frame); connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)), - this, SLOT(propertyContainerAdded(PropertyContainer *))); + this, SLOT(propertyContainerAdded(PropertyContainer *))); connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)), - this, SLOT(propertyContainerRemoved(PropertyContainer *))); + this, SLOT(propertyContainerRemoved(PropertyContainer *))); connect(pane, SIGNAL(paneInteractedWith()), - this, SLOT(paneInteractedWith())); + this, SLOT(paneInteractedWith())); connect(pane, SIGNAL(rightButtonMenuRequested(QPoint)), this, SLOT(rightButtonMenuRequested(QPoint))); connect(pane, SIGNAL(dropAccepted(QStringList)), @@ -181,7 +181,7 @@ emit paneAdded(); if (!m_currentPane) { - setCurrentPane(pane); + setCurrentPane(pane); } showOrHidePaneAccessories(); @@ -294,29 +294,29 @@ QWidget *stack = 0; for (i = m_panes.begin(); i != m_panes.end(); ++i) { - if (i->pane == pane) { + if (i->pane == pane) { stack = i->propertyStack; - m_panes.erase(i); - found = true; - break; - } + m_panes.erase(i); + found = true; + break; + } } if (!found) { - for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) { - if (i->pane == pane) { + for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) { + if (i->pane == pane) { stack = i->propertyStack; - m_hiddenPanes.erase(i); - found = true; - break; - } - } + m_hiddenPanes.erase(i); + found = true; + break; + } + } - if (!found) { - cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << endl; - return; - } + if (!found) { + cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << endl; + return; + } } emit paneAboutToBeDeleted(pane); @@ -337,11 +337,11 @@ delete pane->parent(); if (m_currentPane == pane) { - if (m_panes.size() > 0) { + if (m_panes.size() > 0) { setCurrentPane(m_panes[0].pane); - } else { - setCurrentPane(0); - } + } else { + setCurrentPane(0); + } } showOrHidePaneAccessories(); @@ -381,28 +381,28 @@ std::vector::iterator i = m_panes.begin(); while (i != m_panes.end()) { - if (i->pane == pane) { + if (i->pane == pane) { - m_hiddenPanes.push_back(*i); - m_panes.erase(i); + m_hiddenPanes.push_back(*i); + m_panes.erase(i); - QWidget *pw = dynamic_cast(pane->parent()); - if (pw) pw->hide(); + QWidget *pw = dynamic_cast(pane->parent()); + if (pw) pw->hide(); - if (m_currentPane == pane) { - if (m_panes.size() > 0) { - setCurrentPane(m_panes[0].pane); - } else { - setCurrentPane(0); - } - } - + if (m_currentPane == pane) { + if (m_panes.size() > 0) { + setCurrentPane(m_panes[0].pane); + } else { + setCurrentPane(0); + } + } + showOrHidePaneAccessories(); emit paneHidden(pane); emit paneHidden(); - return; - } - ++i; + return; + } + ++i; } relinkAlignmentViews(); @@ -416,19 +416,19 @@ std::vector::iterator i = m_hiddenPanes.begin(); while (i != m_hiddenPanes.end()) { - if (i->pane == pane) { - m_panes.push_back(*i); - m_hiddenPanes.erase(i); - QWidget *pw = dynamic_cast(pane->parent()); - if (pw) pw->show(); + if (i->pane == pane) { + m_panes.push_back(*i); + m_hiddenPanes.erase(i); + QWidget *pw = dynamic_cast(pane->parent()); + if (pw) pw->show(); - //!!! update current pane + //!!! update current pane showOrHidePaneAccessories(); - return; - } - ++i; + return; + } + ++i; } relinkAlignmentViews(); @@ -456,23 +456,23 @@ bool found = false; while (i != m_panes.end()) { - if (i->pane == pane) { - i->currentIndicator->setPixmap(selectedMap); + if (i->pane == pane) { + i->currentIndicator->setPixmap(selectedMap); if (m_layoutStyle != PropertyStackPerPaneLayout) { m_propertyStackStack->setCurrentWidget(i->propertyStack); } - found = true; - } else { - i->currentIndicator->setPixmap(unselectedMap); - } - ++i; + found = true; + } else { + i->currentIndicator->setPixmap(unselectedMap); + } + ++i; } if (found || pane == 0) { - m_currentPane = pane; - emit currentPaneChanged(m_currentPane); + m_currentPane = pane; + emit currentPaneChanged(m_currentPane); } else { - cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << endl; + cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << endl; } } @@ -483,28 +483,28 @@ if (m_currentPane) { - std::vector::iterator i = m_panes.begin(); + std::vector::iterator i = m_panes.begin(); - while (i != m_panes.end()) { + while (i != m_panes.end()) { - if (i->pane == pane) { - PropertyStack *stack = dynamic_cast - (i->propertyStack); - if (stack) { - if (stack->containsContainer(layer)) { - stack->setCurrentIndex(stack->getContainerIndex(layer)); - emit currentLayerChanged(pane, layer); - } else { - stack->setCurrentIndex - (stack->getContainerIndex - (pane->getPropertyContainer(0))); - emit currentLayerChanged(pane, 0); - } - } - break; - } - ++i; - } + if (i->pane == pane) { + PropertyStack *stack = dynamic_cast + (i->propertyStack); + if (stack) { + if (stack->containsContainer(layer)) { + stack->setCurrentIndex(stack->getContainerIndex(layer)); + emit currentLayerChanged(pane, layer); + } else { + stack->setCurrentIndex + (stack->getContainerIndex + (pane->getPropertyContainer(0))); + emit currentLayerChanged(pane, 0); + } + } + break; + } + ++i; + } } } @@ -532,14 +532,14 @@ std::vector::iterator i = m_panes.begin(); while (i != m_panes.end()) { - PropertyStack *stack = dynamic_cast(i->propertyStack); - if (stack && - stack->getClient() == client && - stack->containsContainer(pc)) { - setCurrentPane(i->pane); - break; - } - ++i; + PropertyStack *stack = dynamic_cast(i->propertyStack); + if (stack && + stack->getClient() == client && + stack->containsContainer(pc)) { + setCurrentPane(i->pane); + break; + } + ++i; } Layer *layer = dynamic_cast(pc); @@ -578,17 +578,17 @@ if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth; for (int i = 0; i < (int)m_panes.size(); ++i) { - if (!m_panes[i].propertyStack) continue; + if (!m_panes[i].propertyStack) continue; #ifdef DEBUG_PANE_STACK - SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min " - << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint " + SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min " + << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint " << m_panes[i].propertyStack->sizeHint().width() << ", current " - << m_panes[i].propertyStack->width() << endl; + << m_panes[i].propertyStack->width() << endl; #endif - if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) { - maxMinWidth = m_panes[i].propertyStack->sizeHint().width(); - } + if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) { + maxMinWidth = m_panes[i].propertyStack->sizeHint().width(); + } } #ifdef DEBUG_PANE_STACK @@ -600,8 +600,8 @@ m_propertyStackStack->setMaximumWidth(setWidth + 10); for (int i = 0; i < (int)m_panes.size(); ++i) { - if (!m_panes[i].propertyStack) continue; - m_panes[i].propertyStack->setMinimumWidth(setWidth); + if (!m_panes[i].propertyStack) continue; + m_panes[i].propertyStack->setMinimumWidth(setWidth); } emit propertyStacksResized(setWidth); @@ -627,7 +627,7 @@ { QObject *s = sender(); for (int i = 0; i < (int)m_panes.size(); ++i) { - if (m_panes[i].xButton == s) { + if (m_panes[i].xButton == s) { emit paneDeleteButtonClicked(m_panes[i].pane); } } @@ -639,7 +639,7 @@ QObject *s = sender(); for (int i = 0; i < (int)m_panes.size(); ++i) { - if (m_panes[i].currentIndicator == s) { + if (m_panes[i].currentIndicator == s) { setCurrentPane(m_panes[i].pane); return; } diff -r 57d192e26331 -r 13d9b422f7fe view/PaneStack.h --- a/view/PaneStack.h Mon Dec 12 15:18:52 2016 +0000 +++ b/view/PaneStack.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _PANESTACK_H_ -#define _PANESTACK_H_ +#ifndef SV_PANESTACK_H +#define SV_PANESTACK_H #include @@ -117,10 +117,10 @@ struct PaneRec { - Pane *pane; - QWidget *propertyStack; + Pane *pane; + QWidget *propertyStack; QPushButton *xButton; - QLabel *currentIndicator; + QLabel *currentIndicator; QFrame *frame; QGridLayout *layout; AlignmentView *alignmentView; diff -r 57d192e26331 -r 13d9b422f7fe view/View.cpp --- a/view/View.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/View.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,8 @@ m_deleting = true; delete m_propertyContainer; + delete m_cache; + delete m_buffer; } PropertyContainer::PropertyList @@ -111,14 +114,14 @@ int View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name, - int *min, int *max, int *deflt) const + int *min, int *max, int *deflt) const { if (deflt) *deflt = 1; if (name == "Global Scroll") return m_followPan; if (name == "Global Zoom") return m_followZoom; if (name == "Follow Playback") { - if (min) *min = 0; - if (max) *max = 2; + if (min) *min = 0; + if (max) *max = 2; if (deflt) *deflt = int(PlaybackScrollPageWithCentre); switch (m_followPlay) { case PlaybackScrollContinuous: return 0; @@ -134,15 +137,15 @@ QString View::getPropertyValueLabel(const PropertyContainer::PropertyName &name, - int value) const + int value) const { if (name == "Follow Playback") { - switch (value) { - default: - case 0: return tr("Scroll"); - case 1: return tr("Page"); - case 2: return tr("Off"); - } + switch (value) { + default: + case 0: return tr("Scroll"); + case 1: return tr("Page"); + case 2: return tr("Off"); + } } return tr(""); } @@ -151,16 +154,16 @@ View::setProperty(const PropertyContainer::PropertyName &name, int value) { if (name == "Global Scroll") { - setFollowGlobalPan(value != 0); + setFollowGlobalPan(value != 0); } else if (name == "Global Zoom") { - setFollowGlobalZoom(value != 0); + setFollowGlobalZoom(value != 0); } else if (name == "Follow Playback") { - switch (value) { - default: - case 0: setPlaybackFollow(PlaybackScrollContinuous); break; - case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break; - case 2: setPlaybackFollow(PlaybackIgnore); break; - } + switch (value) { + default: + case 0: setPlaybackFollow(PlaybackScrollContinuous); break; + case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break; + case 2: setPlaybackFollow(PlaybackIgnore); break; + } } } @@ -174,7 +177,7 @@ View::getPropertyContainer(int i) const { return (const PropertyContainer *)(((View *)this)-> - getPropertyContainer(i)); + getPropertyContainer(i)); } PropertyContainer * @@ -233,11 +236,11 @@ } } - int y = 15 + paint.fontMetrics().ascent(); + int y = ViewManager::scalePixelSize(15) + paint.fontMetrics().ascent(); for (std::map::const_iterator i = sortedLayers.begin(); i != sortedLayers.end(); ++i) { - if (i->second == layer) return y; + if (i->second == layer) break; y += paint.fontMetrics().height(); } @@ -250,11 +253,11 @@ if (client != this) return; if (pc == m_propertyContainer) { - if (m_haveSelectedLayer) { - m_haveSelectedLayer = false; - update(); - } - return; + if (m_haveSelectedLayer) { + m_haveSelectedLayer = false; + update(); + } + return; } delete m_cache; @@ -263,19 +266,19 @@ Layer *selectedLayer = 0; for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if (*i == pc) { - selectedLayer = *i; - m_layerStack.erase(i); - break; - } + if (*i == pc) { + selectedLayer = *i; + m_layerStack.erase(i); + break; + } } if (selectedLayer) { - m_haveSelectedLayer = true; - m_layerStack.push_back(selectedLayer); - update(); + m_haveSelectedLayer = true; + m_layerStack.push_back(selectedLayer); + update(); } else { - m_haveSelectedLayer = false; + m_haveSelectedLayer = false; } emit propertyContainerSelected(pc); @@ -326,23 +329,23 @@ if (m_centreFrame != f) { - int formerPixel = int(m_centreFrame / m_zoomLevel); - - m_centreFrame = f; - - int newPixel = int(m_centreFrame / m_zoomLevel); - - if (newPixel != formerPixel) { + int formerPixel = int(m_centreFrame / m_zoomLevel); + + m_centreFrame = f; + + int newPixel = int(m_centreFrame / m_zoomLevel); + + if (newPixel != formerPixel) { #ifdef DEBUG_VIEW_WIDGET_PAINT - cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl; + cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl; #endif - update(); - - changeVisible = true; - } - - if (e) { + update(); + + changeVisible = true; + } + + if (e) { sv_frame_t rf = alignToReference(f); #ifdef DEBUG_VIEW cerr << "View[" << this << "]::setCentreFrame(" << f @@ -380,9 +383,9 @@ double View::getYForFrequency(double frequency, - double minf, - double maxf, - bool logarithmic) const + double minf, + double maxf, + bool logarithmic) const { Profiler profiler("View::getYForFrequency"); @@ -390,57 +393,57 @@ if (logarithmic) { - static double lastminf = 0.0, lastmaxf = 0.0; - static double logminf = 0.0, logmaxf = 0.0; - - if (lastminf != minf) { - lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10(minf); - } - if (lastmaxf != maxf) { - lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10(maxf); - } - - if (logminf == logmaxf) return 0; - return h - (h * (log10(frequency) - logminf)) / (logmaxf - logminf); + static double lastminf = 0.0, lastmaxf = 0.0; + static double logminf = 0.0, logmaxf = 0.0; + + if (lastminf != minf) { + lastminf = (minf == 0.0 ? 1.0 : minf); + logminf = log10(minf); + } + if (lastmaxf != maxf) { + lastmaxf = (maxf < lastminf ? lastminf : maxf); + logmaxf = log10(maxf); + } + + if (logminf == logmaxf) return 0; + return h - (h * (log10(frequency) - logminf)) / (logmaxf - logminf); } else { - - if (minf == maxf) return 0; - return h - (h * (frequency - minf)) / (maxf - minf); + + if (minf == maxf) return 0; + return h - (h * (frequency - minf)) / (maxf - minf); } } double View::getFrequencyForY(double y, - double minf, - double maxf, - bool logarithmic) const + double minf, + double maxf, + bool logarithmic) const { double h = height(); if (logarithmic) { - static double lastminf = 0.0, lastmaxf = 0.0; - static double logminf = 0.0, logmaxf = 0.0; - - if (lastminf != minf) { - lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10(minf); - } - if (lastmaxf != maxf) { - lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10(maxf); - } - - if (logminf == logmaxf) return 0; - return pow(10.0, logminf + ((logmaxf - logminf) * (h - y)) / h); + static double lastminf = 0.0, lastmaxf = 0.0; + static double logminf = 0.0, logmaxf = 0.0; + + if (lastminf != minf) { + lastminf = (minf == 0.0 ? 1.0 : minf); + logminf = log10(minf); + } + if (lastmaxf != maxf) { + lastmaxf = (maxf < lastminf ? lastminf : maxf); + logmaxf = log10(maxf); + } + + if (logminf == logmaxf) return 0; + return pow(10.0, logminf + ((logmaxf - logminf) * (h - y)) / h); } else { - if (minf == maxf) return 0; - return minf + ((h - y) * (maxf - minf)) / h; + if (minf == maxf) return 0; + return minf + ((h - y) * (maxf - minf)) / h; } } @@ -448,7 +451,7 @@ View::getZoomLevel() const { #ifdef DEBUG_VIEW_WIDGET_PAINT -// cout << "zoom level: " << m_zoomLevel << endl; +// cout << "zoom level: " << m_zoomLevel << endl; #endif return m_zoomLevel; } @@ -479,9 +482,9 @@ if (z < dpratio) return; if (z < 1) z = 1; if (m_zoomLevel != int(z)) { - m_zoomLevel = z; - emit zoomLevelChanged(z, m_followZoom); - update(); + m_zoomLevel = z; + emit zoomLevelChanged(z, m_followZoom); + update(); } } @@ -508,7 +511,7 @@ } } - if (int(maxSignificance) >= int(Layer::ColourAndBackgroundSignificant)) { + if (int(maxSignificance) >= int(Layer::ColourDistinguishes)) { return !mostSignificantHasDarkBackground; } else { return !darkPalette; @@ -593,23 +596,23 @@ pb->hide(); connect(layer, SIGNAL(layerParametersChanged()), - this, SLOT(layerParametersChanged())); + this, SLOT(layerParametersChanged())); connect(layer, SIGNAL(layerParameterRangesChanged()), - this, SLOT(layerParameterRangesChanged())); + this, SLOT(layerParameterRangesChanged())); connect(layer, SIGNAL(layerMeasurementRectsChanged()), - this, SLOT(layerMeasurementRectsChanged())); + this, SLOT(layerMeasurementRectsChanged())); connect(layer, SIGNAL(layerNameChanged()), - this, SLOT(layerNameChanged())); + this, SLOT(layerNameChanged())); connect(layer, SIGNAL(modelChanged()), - this, SLOT(modelChanged())); + this, SLOT(modelChanged())); connect(layer, SIGNAL(modelCompletionChanged()), - this, SLOT(modelCompletionChanged())); + this, SLOT(modelCompletionChanged())); connect(layer, SIGNAL(modelAlignmentCompletionChanged()), - this, SLOT(modelAlignmentCompletionChanged())); + this, SLOT(modelAlignmentCompletionChanged())); connect(layer, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); + this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); connect(layer, SIGNAL(modelReplaced()), - this, SLOT(modelReplaced())); + this, SLOT(modelReplaced())); update(); @@ -620,7 +623,7 @@ View::removeLayer(Layer *layer) { if (m_deleting) { - return; + return; } delete m_cache; @@ -629,8 +632,8 @@ for (LayerList::iterator i = m_fixedOrderLayers.begin(); i != m_fixedOrderLayers.end(); ++i) { - if (*i == layer) { - m_fixedOrderLayers.erase(i); + if (*i == layer) { + m_fixedOrderLayers.erase(i); break; } } @@ -638,16 +641,16 @@ for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if (*i == layer) { - m_layerStack.erase(i); - if (m_progressBars.find(layer) != m_progressBars.end()) { - delete m_progressBars[layer].bar; + if (*i == layer) { + m_layerStack.erase(i); + if (m_progressBars.find(layer) != m_progressBars.end()) { + delete m_progressBars[layer].bar; delete m_progressBars[layer].cancel; - delete m_progressBars[layer].checkTimer; - m_progressBars.erase(layer); - } - break; - } + delete m_progressBars[layer].checkTimer; + m_progressBars.erase(layer); + } + break; + } } disconnect(layer, SIGNAL(layerParametersChanged()), @@ -704,7 +707,7 @@ if (m_haveSelectedLayer && !m_layerStack.empty()) { return getLayer(getLayerCount() - 1); } else { - return 0; + return 0; } } @@ -718,36 +721,36 @@ View::setViewManager(ViewManager *manager) { if (m_manager) { - m_manager->disconnect(this, SLOT(globalCentreFrameChanged(sv_frame_t))); - m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); - m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); - m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool))); + m_manager->disconnect(this, SLOT(globalCentreFrameChanged(sv_frame_t))); + m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); + m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); + m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool))); m_manager->disconnect(this, SLOT(toolModeChanged())); m_manager->disconnect(this, SLOT(selectionChanged())); m_manager->disconnect(this, SLOT(overlayModeChanged())); m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged())); disconnect(m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode))); - disconnect(m_manager, SLOT(zoomLevelChanged(ZoomLevel, bool))); + disconnect(m_manager, SLOT(zoomLevelChanged(ZoomLevel, bool))); } m_manager = manager; connect(m_manager, SIGNAL(globalCentreFrameChanged(sv_frame_t)), - this, SLOT(globalCentreFrameChanged(sv_frame_t))); + this, SLOT(globalCentreFrameChanged(sv_frame_t))); connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)), - this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); + this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); connect(m_manager, SIGNAL(playbackFrameChanged(sv_frame_t)), - this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); + this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, ZoomLevel, bool)), - this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool))); + this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool))); connect(m_manager, SIGNAL(toolModeChanged()), - this, SLOT(toolModeChanged())); + this, SLOT(toolModeChanged())); connect(m_manager, SIGNAL(selectionChanged()), - this, SLOT(selectionChanged())); + this, SLOT(selectionChanged())); connect(m_manager, SIGNAL(inProgressSelectionChanged()), - this, SLOT(selectionChanged())); + this, SLOT(selectionChanged())); connect(m_manager, SIGNAL(overlayModeChanged()), this, SLOT(overlayModeChanged())); connect(m_manager, SIGNAL(showCentreLineChanged()), @@ -761,7 +764,7 @@ PlaybackFollowMode))); connect(this, SIGNAL(zoomLevelChanged(ZoomLevel, bool)), - m_manager, SLOT(viewZoomLevelChanged(ZoomLevel, bool))); + m_manager, SLOT(viewZoomLevelChanged(ZoomLevel, bool))); switch (m_followPlay) { @@ -833,16 +836,16 @@ bool discard; LayerList scrollables = getScrollableBackLayers(false, discard); for (LayerList::const_iterator i = scrollables.begin(); - i != scrollables.end(); ++i) { - if (*i == obj || (*i)->getModel() == obj) { - recreate = true; - break; - } + i != scrollables.end(); ++i) { + if (*i == obj || (*i)->getModel() == obj) { + recreate = true; + break; + } } if (recreate) { - delete m_cache; - m_cache = 0; + delete m_cache; + m_cache = 0; } emit layerModelChanged(); @@ -865,12 +868,12 @@ #endif if (myStartFrame > 0 && endFrame < myStartFrame) { - checkProgress(obj); - return; + checkProgress(obj); + return; } if (startFrame > myEndFrame) { - checkProgress(obj); - return; + checkProgress(obj); + return; } // If the model that has changed is not used by any of the cached @@ -881,16 +884,16 @@ bool discard; LayerList scrollables = getScrollableBackLayers(false, discard); for (LayerList::const_iterator i = scrollables.begin(); - i != scrollables.end(); ++i) { - if (*i == obj || (*i)->getModel() == obj) { - recreate = true; - break; - } + i != scrollables.end(); ++i) { + if (*i == obj || (*i)->getModel() == obj) { + recreate = true; + break; + } } if (recreate) { - delete m_cache; - m_cache = 0; + delete m_cache; + m_cache = 0; } if (startFrame < myStartFrame) startFrame = myStartFrame; @@ -945,7 +948,7 @@ update(); if (layer) { - emit propertyContainerPropertyChanged(layer); + emit propertyContainerPropertyChanged(layer); } } @@ -993,7 +996,7 @@ View::viewManagerPlaybackFrameChanged(sv_frame_t f) { if (m_manager) { - if (sender() != m_manager) return; + if (sender() != m_manager) return; } #ifdef DEBUG_VIEW @@ -1028,7 +1031,7 @@ (QApplication::keyboardModifiers() & Qt::AltModifier)); bool pointerInVisibleArea = - long(m_playPointerFrame) >= getStartFrame() && + long(m_playPointerFrame) >= getStartFrame() && (m_playPointerFrame < getEndFrame() || // include old pointer location so we know to refresh when moving out oldPlayPointerFrame < getEndFrame()); @@ -1036,10 +1039,10 @@ switch (m_followPlay) { case PlaybackScrollContinuous: - if (!somethingGoingOn) { - setCentreFrame(m_playPointerFrame, false); - } - break; + if (!somethingGoingOn) { + setCentreFrame(m_playPointerFrame, false); + } + break; case PlaybackScrollPage: case PlaybackScrollPageWithCentre: @@ -1114,11 +1117,11 @@ break; case PlaybackIgnore: - if (m_playPointerFrame >= getStartFrame() && + if (m_playPointerFrame >= getStartFrame() && m_playPointerFrame < getEndFrame()) { - update(); - } - break; + update(); + } + break; } } @@ -1137,9 +1140,9 @@ View::selectionChanged() { if (m_selectionCached) { - delete m_cache; - m_cache = 0; - m_selectionCached = false; + delete m_cache; + m_cache = 0; + m_selectionCached = false; } update(); } @@ -1170,15 +1173,15 @@ for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - - sv_frame_t thisStartFrame = (*i)->getModel()->getStartFrame(); - - if (first || thisStartFrame < startFrame) { - startFrame = thisStartFrame; - } - first = false; - } + if ((*i)->getModel() && (*i)->getModel()->isOK()) { + + sv_frame_t thisStartFrame = (*i)->getModel()->getStartFrame(); + + if (first || thisStartFrame < startFrame) { + startFrame = thisStartFrame; + } + first = false; + } } return startFrame; } @@ -1191,15 +1194,15 @@ for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - - sv_frame_t thisEndFrame = (*i)->getModel()->getEndFrame(); - - if (first || thisEndFrame > endFrame) { - endFrame = thisEndFrame; - } - first = false; - } + if ((*i)->getModel() && (*i)->getModel()->isOK()) { + + sv_frame_t thisEndFrame = (*i)->getModel()->getEndFrame(); + + if (first || thisEndFrame > endFrame) { + endFrame = thisEndFrame; + } + first = false; + } } if (first) return getModelsStartFrame(); @@ -1216,9 +1219,9 @@ //!!! nah, this wants to always return the sr of the main model! for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - return (*i)->getModel()->getSampleRate(); - } + if ((*i)->getModel() && (*i)->getModel()->isOK()) { + return (*i)->getModel()->getSampleRate(); + } } return 0; } @@ -1323,7 +1326,7 @@ { // True iff all views are scrollable for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if (!(*i)->isLayerScrollable(this)) return false; + if (!(*i)->isLayerScrollable(this)) return false; } return true; } @@ -1344,13 +1347,13 @@ // cerr << "(name is " << (*i)->objectName() << ")" // << endl; // SVDEBUG << "View::getScrollableBackLayers: I am " << this << endl; - if ((*i)->isLayerDormant(this)) continue; - if ((*i)->isLayerOpaque()) { - // You can't see anything behind an opaque layer! - scrollables.clear(); + if ((*i)->isLayerDormant(this)) continue; + if ((*i)->isLayerOpaque()) { + // You can't see anything behind an opaque layer! + scrollables.clear(); if (metUnscrollable) break; - } - if (!metUnscrollable && (*i)->isLayerScrollable(this)) { + } + if (!metUnscrollable && (*i)->isLayerScrollable(this)) { scrollables.push_back(*i); } else { metUnscrollable = true; @@ -1358,8 +1361,8 @@ } if (testChanged && scrollables != m_lastScrollableBackLayers) { - m_lastScrollableBackLayers = scrollables; - changed = true; + m_lastScrollableBackLayers = scrollables; + changed = true; } return scrollables; } @@ -1376,21 +1379,21 @@ bool started = false; for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if ((*i)->isLayerDormant(this)) continue; - if (!started && (*i)->isLayerScrollable(this)) { - continue; - } - started = true; - if ((*i)->isLayerOpaque()) { - // You can't see anything behind an opaque layer! - nonScrollables.clear(); - } - nonScrollables.push_back(*i); + if ((*i)->isLayerDormant(this)) continue; + if (!started && (*i)->isLayerScrollable(this)) { + continue; + } + started = true; + if ((*i)->isLayerOpaque()) { + // You can't see anything behind an opaque layer! + nonScrollables.clear(); + } + nonScrollables.push_back(*i); } if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) { - m_lastNonScrollableBackLayers = nonScrollables; - changed = true; + m_lastNonScrollableBackLayers = nonScrollables; + changed = true; } return nonScrollables; @@ -1398,7 +1401,7 @@ int View::getZoomConstraintBlockSize(int blockSize, - ZoomConstraint::RoundingDirection dir) + ZoomConstraint::RoundingDirection dir) const { int candidate = blockSize; @@ -1408,20 +1411,20 @@ for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); - if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; - - int thisBlockSize = - zoomConstraint->getNearestBlockSize(blockSize, dir); - - // Go for the block size that's furthest from the one - // passed in. Most of the time, that's what we want. - if (!haveCandidate || - (thisBlockSize > blockSize && thisBlockSize > candidate) || - (thisBlockSize < blockSize && thisBlockSize < candidate)) { - candidate = thisBlockSize; - haveCandidate = true; - } + const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); + if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; + + int thisBlockSize = + zoomConstraint->getNearestBlockSize(blockSize, dir); + + // Go for the block size that's furthest from the one + // passed in. Most of the time, that's what we want. + if (!haveCandidate || + (thisBlockSize > blockSize && thisBlockSize > candidate) || + (thisBlockSize < blockSize && thisBlockSize < candidate)) { + candidate = thisBlockSize; + haveCandidate = true; + } } return candidate; @@ -1431,7 +1434,7 @@ View::areLayerColoursSignificant() const { for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if ((*i)->getLayerColourSignificance() == + if ((*i)->getLayerColourSignificance() == Layer::ColourHasMeaningfulValue) return true; if ((*i)->isLayerOpaque()) break; } @@ -1453,15 +1456,15 @@ ZoomLevel newZoomLevel = m_zoomLevel; if (in) { - newZoomLevel = getZoomConstraintLevel(m_zoomLevel.decremented(), + newZoomLevel = getZoomConstraintLevel(m_zoomLevel.decremented(), ZoomConstraint::RoundDown); } else { - newZoomLevel = getZoomConstraintLevel(m_zoomLevel.incremented(), + newZoomLevel = getZoomConstraintLevel(m_zoomLevel.incremented(), ZoomConstraint::RoundUp); } if (newZoomLevel != m_zoomLevel) { - setZoomLevel(newZoomLevel); + setZoomLevel(newZoomLevel); } } @@ -1470,18 +1473,18 @@ { sv_frame_t delta; if (lots) { - delta = (getEndFrame() - getStartFrame()) / 2; + delta = (getEndFrame() - getStartFrame()) / 2; } else { - delta = (getEndFrame() - getStartFrame()) / 20; + delta = (getEndFrame() - getStartFrame()) / 20; } if (right) delta = -delta; if (m_centreFrame < delta) { - setCentreFrame(0, e); + setCentreFrame(0, e); } else if (m_centreFrame - delta >= getModelsEndFrame()) { - setCentreFrame(getModelsEndFrame(), e); + setCentreFrame(getModelsEndFrame(), e); } else { - setCentreFrame(m_centreFrame - delta, e); + setCentreFrame(m_centreFrame - delta, e); } } @@ -1492,7 +1495,7 @@ if (!cancel) return; for (ProgressMap::iterator i = m_progressBars.begin(); - i != m_progressBars.end(); ++i) { + i != m_progressBars.end(); ++i) { if (i->second.cancel == cancel) { @@ -1512,19 +1515,19 @@ int ph = height(); for (ProgressMap::iterator i = m_progressBars.begin(); - i != m_progressBars.end(); ++i) { + i != m_progressBars.end(); ++i) { QProgressBar *pb = i->second.bar; QPushButton *cancel = i->second.cancel; - if (i->first == object) { + if (i->first == object) { // 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 QTimer *timer = i->second.checkTimer; - int completion = i->first->getCompletion(this); + int completion = i->first->getCompletion(this); QString text = i->first->getPropertyContainerName(); QString error = i->first->getError(this); @@ -1559,13 +1562,13 @@ update(); // ensure duration &c gets updated } - if (completion >= 100) { - - pb->hide(); + if (completion >= 100) { + + pb->hide(); cancel->hide(); timer->stop(); - } else { + } else { // cerr << "progress = " << completion << endl; @@ -1578,19 +1581,19 @@ cancel->move(0, ph - pb->height()/2 - 10); cancel->show(); - pb->setValue(completion); - pb->move(20, ph - pb->height()); - - pb->show(); - pb->update(); - - ph -= pb->height(); - } - } else { - if (pb->isVisible()) { - ph -= pb->height(); - } - } + pb->setValue(completion); + pb->move(20, ph - pb->height()); + + pb->show(); + pb->update(); + + ph -= pb->height(); + } + } else { + if (pb->isVisible()) { + ph -= pb->height(); + } + } } } @@ -1617,7 +1620,7 @@ View::getProgressBarWidth() const { for (ProgressMap::const_iterator i = m_progressBars.begin(); - i != m_progressBars.end(); ++i) { + i != m_progressBars.end(); ++i) { if (i->second.bar && i->second.bar->isVisible()) { return i->second.bar->width(); } @@ -1657,8 +1660,8 @@ // cerr << "View::paintEvent: centre frame is " << m_centreFrame << endl; if (m_layerStack.empty()) { - QFrame::paintEvent(e); - return; + QFrame::paintEvent(e); + return; } // ensure our constraints are met @@ -1667,7 +1670,7 @@ zoom levels? m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, - ZoomConstraint::RoundUp); + ZoomConstraint::RoundUp); */ QPainter paint; @@ -1677,10 +1680,10 @@ QRect cacheRect(rect()); if (e) { - cacheRect &= e->rect(); + cacheRect &= e->rect(); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() - << ", my rect " << width() << "x" << height() << endl; + cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() + << ", my rect " << width() << "x" << height() << endl; #endif } @@ -1731,17 +1734,17 @@ #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() - << " scrollable back layers and " << nonScrollables.size() - << " non-scrollable front layers" << endl; + << " scrollable back layers and " << nonScrollables.size() + << " non-scrollable front layers" << endl; cerr << "haveSelections " << haveSelections << ", selectionCacheable " - << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; + << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; #endif if (layersChanged || scrollables.empty() || - (haveSelections && (selectionCacheable != m_selectionCached))) { - delete m_cache; - m_cache = 0; - m_selectionCached = false; + (haveSelections && (selectionCacheable != m_selectionCached))) { + delete m_cache; + m_cache = 0; + m_selectionCached = false; } QSize scaledCacheSize(scaledSize(size(), dpratio)); @@ -1759,75 +1762,75 @@ << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl; #endif - if (!m_cache || - m_cacheZoomLevel != m_zoomLevel || + if (!m_cache || + m_cacheZoomLevel != m_zoomLevel || scaledCacheSize != m_cache->size()) { - // cache is not valid - - if (cacheRect.width() < width()/10) { - delete m_cache; + // cache is not valid + + if (cacheRect.width() < width()/10) { + delete m_cache; m_cache = 0; #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; + cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; #endif - } else { - delete m_cache; - m_cache = new QPixmap(scaledCacheSize); + } else { + delete m_cache; + m_cache = new QPixmap(scaledCacheSize); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; + cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; #endif - cacheRect = rect(); - repaintCache = true; - } - - } else if (m_cacheCentreFrame != m_centreFrame) { - - int dx = - getXForFrame(m_cacheCentreFrame) - - getXForFrame(m_centreFrame); - - if (dx > -width() && dx < width()) { - static QPixmap *tmpPixmap = 0; - if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { - delete tmpPixmap; - tmpPixmap = new QPixmap(scaledCacheSize); - } - paint.begin(tmpPixmap); - paint.drawPixmap(0, 0, *m_cache); - paint.end(); - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *tmpPixmap); - paint.end(); - if (dx < 0) { - cacheRect = QRect(width() + dx, 0, -dx, height()); - } else { - cacheRect = QRect(0, 0, dx, height()); - } + cacheRect = rect(); + repaintCache = true; + } + + } else if (m_cacheCentreFrame != m_centreFrame) { + + int dx = + getXForFrame(m_cacheCentreFrame) - + getXForFrame(m_centreFrame); + + if (dx > -width() && dx < width()) { + static QPixmap *tmpPixmap = 0; + if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { + delete tmpPixmap; + tmpPixmap = new QPixmap(scaledCacheSize); + } + paint.begin(tmpPixmap); + paint.drawPixmap(0, 0, *m_cache); + paint.end(); + paint.begin(m_cache); + paint.drawPixmap(dx, 0, *tmpPixmap); + paint.end(); + if (dx < 0) { + cacheRect = QRect(width() + dx, 0, -dx, height()); + } else { + cacheRect = QRect(0, 0, dx, height()); + } #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; + cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; #endif - } else { - cacheRect = rect(); + } else { + cacheRect = rect(); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; + cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; #endif - } - repaintCache = true; - - } else { + } + repaintCache = true; + + } else { #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: cache is good" << endl; + cerr << "View(" << this << ")::paintEvent: cache is good" << endl; #endif - paint.begin(m_buffer); - paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); - paint.end(); - QFrame::paintEvent(e); - paintedCacheRect = true; - } - - m_cacheCentreFrame = m_centreFrame; - m_cacheZoomLevel = m_zoomLevel; + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); + paint.end(); + QFrame::paintEvent(e); + paintedCacheRect = true; + } + + m_cacheCentreFrame = m_centreFrame; + m_cacheZoomLevel = m_zoomLevel; } #ifdef DEBUG_VIEW_WIDGET_PAINT @@ -1842,7 +1845,7 @@ QRect rectToPaint; - if (repaintCache) { + if (repaintCache) { paint.begin(m_cache); rectToPaint = scaledCacheRect; } else { @@ -1851,39 +1854,39 @@ } setPaintFont(paint); - paint.setClipRect(rectToPaint); + paint.setClipRect(rectToPaint); paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(rectToPaint); - - paint.setPen(getForeground()); - paint.setBrush(Qt::NoBrush); - - for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { - paint.setRenderHint(QPainter::Antialiasing, false); - paint.save(); + paint.drawRect(rectToPaint); + + paint.setPen(getForeground()); + paint.setBrush(Qt::NoBrush); + + for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { + paint.setRenderHint(QPainter::Antialiasing, false); + paint.save(); #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl; #endif (*i)->paint(&proxy, paint, rectToPaint); - paint.restore(); - } - - if (haveSelections && selectionCacheable) { - drawSelections(paint); - m_selectionCached = repaintCache; - } - - paint.end(); - - if (repaintCache) { - cacheRect |= (e ? e->rect() : rect()); + paint.restore(); + } + + if (haveSelections && selectionCacheable) { + drawSelections(paint); + m_selectionCached = repaintCache; + } + + paint.end(); + + if (repaintCache) { + cacheRect |= (e ? e->rect() : rect()); scaledCacheRect = scaledRect(cacheRect, dpratio); - paint.begin(m_buffer); - paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); - paint.end(); - } + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); + paint.end(); + } } // Now non-cacheable items. We always need to redraw the @@ -1900,20 +1903,20 @@ if (scrollables.empty()) { paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(scaledNonCacheRect); + paint.drawRect(scaledNonCacheRect); } - + paint.setPen(getForeground()); paint.setBrush(Qt::NoBrush); - + for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { // Profiler profiler2("View::paintEvent non-cacheable"); #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl; #endif - (*i)->paint(&proxy, paint, scaledNonCacheRect); + (*i)->paint(&proxy, paint, scaledNonCacheRect); } - + paint.end(); paint.begin(this); @@ -1925,7 +1928,7 @@ setPaintFont(paint); if (e) paint.setClipRect(e->rect()); if (!m_selectionCached) { - drawSelections(paint); + drawSelections(paint); } paint.end(); @@ -1947,7 +1950,7 @@ if (showPlayPointer) { - paint.begin(this); + paint.begin(this); int playx = getXForFrame(m_playPointerFrame); @@ -1959,7 +1962,7 @@ paint.setPen(getBackground()); paint.drawLine(playx, 1, playx, height() - 2); - paint.end(); + paint.end(); } QFrame::paintEvent(e); @@ -1973,14 +1976,14 @@ MultiSelection::SelectionList selections; if (m_manager) { - selections = m_manager->getSelections(); - if (m_manager->haveInProgressSelection()) { - bool exclusive; - Selection inProgressSelection = - m_manager->getInProgressSelection(exclusive); - if (exclusive) selections.clear(); - selections.insert(inProgressSelection); - } + selections = m_manager->getSelections(); + if (m_manager->haveInProgressSelection()) { + bool exclusive; + Selection inProgressSelection = + m_manager->getInProgressSelection(exclusive); + if (exclusive) selections.clear(); + selections.insert(inProgressSelection); + } } paint.save(); @@ -2000,113 +2003,123 @@ bool closeToLeft, closeToRight; if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { - illuminateFrame = getFrameForX(localPos.x()); + illuminateFrame = getFrameForX(localPos.x()); } const QFontMetrics &metrics = paint.fontMetrics(); for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - - int p0 = getXForFrame(alignFromReference(i->getStartFrame())); - int p1 = getXForFrame(alignFromReference(i->getEndFrame())); - - if (p1 < 0 || p0 > width()) continue; + i != selections.end(); ++i) { + + int p0 = getXForFrame(alignFromReference(i->getStartFrame())); + int p1 = getXForFrame(alignFromReference(i->getEndFrame())); + + if (p1 < 0 || p0 > width()) continue; #ifdef DEBUG_VIEW_WIDGET_PAINT - SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl; + SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl; #endif - bool illuminateThis = - (illuminateFrame >= 0 && i->contains(illuminateFrame)); - - paint.setPen(QColor(150, 150, 255)); + bool illuminateThis = + (illuminateFrame >= 0 && i->contains(illuminateFrame)); + + double h = height(); + double penWidth = PaintAssistant::scalePenWidth(1.0); + double half = penWidth/2.0; + + paint.setPen(QPen(QColor(150, 150, 255), penWidth)); if (translucent && shouldLabelSelections()) { - paint.drawRect(p0, -1, p1 - p0, height() + 1); + paint.drawRect(QRectF(p0, -penWidth, p1 - p0, h + 2*penWidth)); } else { // Make the top & bottom lines of the box visible if we // are lacking some of the other visual cues. There's no // particular logic to this, it's just a question of what // I happen to think looks nice. - paint.drawRect(p0, 0, p1 - p0, height() - 1); + paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth)); } - if (illuminateThis) { - paint.save(); - paint.setPen(QPen(getForeground(), 2)); - if (closeToLeft) { - paint.drawLine(p0, 1, p1, 1); - paint.drawLine(p0, 0, p0, height()); - paint.drawLine(p0, height() - 1, p1, height() - 1); - } else if (closeToRight) { - paint.drawLine(p0, 1, p1, 1); - paint.drawLine(p1, 0, p1, height()); - paint.drawLine(p0, height() - 1, p1, height() - 1); - } else { - paint.setBrush(Qt::NoBrush); - paint.drawRect(p0, 1, p1 - p0, height() - 2); - } - paint.restore(); - } - - if (sampleRate && shouldLabelSelections() && m_manager && + if (illuminateThis) { + paint.save(); + penWidth = PaintAssistant::scalePenWidth(2.0); + half = penWidth/2.0; + paint.setPen(QPen(getForeground(), penWidth)); + if (closeToLeft) { + paint.drawLine(QLineF(p0, half, p1, half)); + paint.drawLine(QLineF(p0, half, p0, h - half)); + paint.drawLine(QLineF(p0, h - half, p1, h - half)); + } else if (closeToRight) { + paint.drawLine(QLineF(p0, half, p1, half)); + paint.drawLine(QLineF(p1, half, p1, h - half)); + paint.drawLine(QLineF(p0, h - half, p1, h - half)); + } else { + paint.setBrush(Qt::NoBrush); + paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth)); + } + paint.restore(); + } + + if (sampleRate && shouldLabelSelections() && m_manager && m_manager->shouldShowSelectionExtents()) { - - QString startText = QString("%1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getStartFrame(), sampleRate).toText(true))) - .arg(i->getStartFrame()); - - QString endText = QString(" %1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getEndFrame(), sampleRate).toText(true))) - .arg(i->getEndFrame()); - - QString durationText = QString("(%1 / %2) ") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getEndFrame() - i->getStartFrame(), sampleRate) - .toText(true))) - .arg(i->getEndFrame() - i->getStartFrame()); - - int sw = metrics.width(startText), - ew = metrics.width(endText), - dw = metrics.width(durationText); - - int sy = metrics.ascent() + metrics.height() + 4; - int ey = sy; - int dy = sy + metrics.height(); - - int sx = p0 + 2; - int ex = sx; - int dx = sx; + + QString startText = QString("%1 / %2") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getStartFrame(), sampleRate).toText(true))) + .arg(i->getStartFrame()); + + QString endText = QString(" %1 / %2") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getEndFrame(), sampleRate).toText(true))) + .arg(i->getEndFrame()); + + QString durationText = QString("(%1 / %2) ") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getEndFrame() - i->getStartFrame(), sampleRate) + .toText(true))) + .arg(i->getEndFrame() - i->getStartFrame()); + + int sw = metrics.width(startText), + ew = metrics.width(endText), + dw = metrics.width(durationText); + + int sy = metrics.ascent() + metrics.height() + 4; + int ey = sy; + int dy = sy + metrics.height(); + + int sx = p0 + 2; + int ex = sx; + int dx = sx; bool durationBothEnds = true; - if (sw + ew > (p1 - p0)) { - ey += metrics.height(); - dy += metrics.height(); + if (sw + ew > (p1 - p0)) { + ey += metrics.height(); + dy += metrics.height(); durationBothEnds = false; - } - - if (ew < (p1 - p0)) { - ex = p1 - 2 - ew; - } - - if (dw < (p1 - p0)) { - dx = p1 - 2 - dw; - } - - paint.drawText(sx, sy, startText); - paint.drawText(ex, ey, endText); - paint.drawText(dx, dy, durationText); + } + + if (ew < (p1 - p0)) { + ex = p1 - 2 - ew; + } + + if (dw < (p1 - p0)) { + dx = p1 - 2 - dw; + } + + PaintAssistant::drawVisibleText(this, paint, sx, sy, startText, + PaintAssistant::OutlinedText); + PaintAssistant::drawVisibleText(this, paint, ex, ey, endText, + PaintAssistant::OutlinedText); + PaintAssistant::drawVisibleText(this, paint, dx, dy, durationText, + PaintAssistant::OutlinedText); if (durationBothEnds) { - paint.drawText(sx, dy, durationText); + PaintAssistant::drawVisibleText(this, paint, sx, dy, durationText, + PaintAssistant::OutlinedText); } - } + } } paint.restore(); @@ -2424,12 +2437,12 @@ paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(QRect(xorigin + x, 0, width(), height())); - - paint.setPen(getForeground()); - paint.setBrush(Qt::NoBrush); - - for (LayerList::iterator i = m_layerStack.begin(); + paint.drawRect(QRect(xorigin + x, 0, width(), height())); + + paint.setPen(getForeground()); + paint.setBrush(Qt::NoBrush); + + for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if (!((*i)->isLayerDormant(this))){ @@ -2448,7 +2461,7 @@ paint.restore(); } - } + } } m_centreFrame = origCentreFrame; @@ -2457,16 +2470,16 @@ } QImage * -View::toNewImage() +View::renderToNewImage() { sv_frame_t f0 = getModelsStartFrame(); sv_frame_t f1 = getModelsEndFrame(); - return toNewImage(f0, f1); + return renderPartToNewImage(f0, f1); } QImage * -View::toNewImage(sv_frame_t f0, sv_frame_t f1) +View::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1) { int x0 = int(f0 / getZoomLevel()); int x1 = int(f1 / getZoomLevel()); @@ -2485,16 +2498,16 @@ } QSize -View::getImageSize() +View::getRenderedImageSize() { sv_frame_t f0 = getModelsStartFrame(); sv_frame_t f1 = getModelsEndFrame(); - return getImageSize(f0, f1); + return getRenderedPartImageSize(f0, f1); } QSize -View::getImageSize(sv_frame_t f0, sv_frame_t f1) +View::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1) { int x0 = int(f0 / getZoomLevel()); int x1 = int(f1 / getZoomLevel()); @@ -2502,6 +2515,35 @@ return QSize(x1 - x0, height()); } +bool +View::renderToSvgFile(QString filename) +{ + sv_frame_t f0 = getModelsStartFrame(); + sv_frame_t f1 = getModelsEndFrame(); + + return renderPartToSvgFile(filename, f0, f1); +} + +bool +View::renderPartToSvgFile(QString filename, sv_frame_t f0, sv_frame_t f1) +{ + int x0 = int(f0 / getZoomLevel()); + int x1 = int(f1 / getZoomLevel()); + + QSvgGenerator generator; + generator.setFileName(filename); + generator.setSize(QSize(x1 - x0, height())); + generator.setViewBox(QRect(0, 0, x1 - x0, height())); + generator.setTitle(tr("Exported image from %1") + .arg(QApplication::applicationName())); + + QPainter paint; + paint.begin(&generator); + bool result = render(paint, 0, f0, f1); + paint.end(); + return result; +} + void View::toXml(QTextStream &stream, QString indent, QString extraAttributes) const @@ -2515,15 +2557,15 @@ "followZoom=\"%4\" " "tracking=\"%5\" " " %6>\n") - .arg(m_centreFrame) - .arg(m_zoomLevel) - .arg(m_followPan) - .arg(m_followZoom) - .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : - m_followPlay == PlaybackScrollPageWithCentre ? "page" : - m_followPlay == PlaybackScrollPage ? "daw" : + .arg(m_centreFrame) + .arg(m_zoomLevel) + .arg(m_followPan) + .arg(m_followZoom) + .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : + m_followPlay == PlaybackScrollPageWithCentre ? "page" : + m_followPlay == PlaybackScrollPage ? "daw" : "ignore") - .arg(extraAttributes); + .arg(extraAttributes); for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) { bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this); @@ -2540,7 +2582,7 @@ { // cerr << "ViewPropertyContainer: " << this << " is owned by View " << v << endl; connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); + this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); } ViewPropertyContainer::~ViewPropertyContainer() diff -r 57d192e26331 -r 13d9b422f7fe view/View.h --- a/view/View.h Mon Dec 12 15:18:52 2016 +0000 +++ b/view/View.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _VIEW_H_ -#define _VIEW_H_ +#ifndef SV_VIEW_H +#define SV_VIEW_H #include #include @@ -51,7 +51,7 @@ */ class View : public QFrame, - public XmlExportable, + public XmlExportable, public LayerGeometryProvider { Q_OBJECT @@ -136,7 +136,7 @@ * Not thread-safe in logarithmic mode. Call only from GUI thread. */ double getYForFrequency(double frequency, double minFreq, double maxFreq, - bool logarithmic) const; + bool logarithmic) const; /** * Return the closest frequency to the given pixel y-coordinate, @@ -274,10 +274,10 @@ return m_manager && m_manager->shouldShowFeatureLabels(); } virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const { - return false; + return false; } virtual bool shouldIlluminateLocalSelection(QPoint &, bool &, bool &) const { - return false; + return false; } virtual void setPlaybackFollow(PlaybackFollowMode m); @@ -293,12 +293,12 @@ virtual QString getPropertyLabel(const PropertyName &) const; virtual PropertyContainer::PropertyType getPropertyType(const PropertyName &) const; virtual int getPropertyRangeAndValue(const PropertyName &, - int *min, int *max, int *deflt) const; + int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; virtual void setProperty(const PropertyName &, int value); virtual QString getPropertyContainerName() const { - return objectName(); + return objectName(); } virtual QString getPropertyContainerIconName() const = 0; @@ -309,12 +309,42 @@ virtual const PropertyContainer *getPropertyContainer(int i) const; virtual PropertyContainer *getPropertyContainer(int i); - // Render the contents on a wide canvas - virtual QImage *toNewImage(sv_frame_t f0, sv_frame_t f1); - virtual QImage *toNewImage(); - virtual QSize getImageSize(sv_frame_t f0, sv_frame_t f1); - virtual QSize getImageSize(); + /** + * Render the view contents to a new QImage (which may be wider + * than the visible View). + */ + virtual QImage *renderToNewImage(); + /** + * Render the view contents between the given frame extents to a + * new QImage (which may be wider than the visible View). + */ + virtual QImage *renderPartToNewImage(sv_frame_t f0, sv_frame_t f1); + + /** + * Calculate and return the size of image that will be generated + * by renderToNewImage(). + */ + virtual QSize getRenderedImageSize(); + + /** + * Calculate and return the size of image that will be generated + * by renderPartToNewImage(f0, f1). + */ + virtual QSize getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1); + + /** + * Render the view contents to a new SVG file. + */ + virtual bool renderToSvgFile(QString filename); + + /** + * Render the view contents between the given frame extents to a + * new SVG file. + */ + virtual bool renderPartToSvgFile(QString filename, + sv_frame_t f0, sv_frame_t f1); + virtual int getTextLabelHeight(const Layer *layer, QPainter &) const; virtual bool getValueExtents(QString unit, double &min, double &max, @@ -461,8 +491,8 @@ bool m_lightBackground; bool m_showProgress; - QPixmap *m_cache; - QPixmap *m_buffer; + QPixmap *m_cache; // I own this + QPixmap *m_buffer; // I own this sv_frame_t m_cacheCentreFrame; ZoomLevel m_cacheZoomLevel; bool m_selectionCached; @@ -510,25 +540,25 @@ return m_v->getPropertyLabel(n); } PropertyType getPropertyType(const PropertyName &n) const { - return m_v->getPropertyType(n); + return m_v->getPropertyType(n); } int getPropertyRangeAndValue(const PropertyName &n, int *min, int *max, int *deflt) const { - return m_v->getPropertyRangeAndValue(n, min, max, deflt); + return m_v->getPropertyRangeAndValue(n, min, max, deflt); } QString getPropertyValueLabel(const PropertyName &n, int value) const { - return m_v->getPropertyValueLabel(n, value); + return m_v->getPropertyValueLabel(n, value); } QString getPropertyContainerName() const { - return m_v->getPropertyContainerName(); + return m_v->getPropertyContainerName(); } QString getPropertyContainerIconName() const { - return m_v->getPropertyContainerIconName(); + return m_v->getPropertyContainerIconName(); } public slots: virtual void setProperty(const PropertyName &n, int value) { - m_v->setProperty(n, value); + m_v->setProperty(n, value); } protected: diff -r 57d192e26331 -r 13d9b422f7fe view/ViewManager.cpp --- a/view/ViewManager.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/view/ViewManager.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -15,6 +15,7 @@ #include "ViewManager.h" #include "base/AudioPlaySource.h" +#include "base/AudioRecordTarget.h" #include "base/RealTime.h" #include "data/model/Model.h" #include "widgets/CommandHistory.h" @@ -30,6 +31,7 @@ ViewManager::ViewManager() : m_playSource(0), + m_recordTarget(0), m_globalCentreFrame(0), m_globalZoom(1024), m_playbackFrame(0), @@ -157,8 +159,20 @@ sv_frame_t ViewManager::getPlaybackFrame() const { - if (m_playSource && m_playSource->isPlaying()) { - m_playbackFrame = m_playSource->getCurrentPlayingFrame(); + if (isRecording()) { + m_playbackFrame = m_recordTarget->getRecordDuration(); +#ifdef DEBUG_VIEW_MANAGER + cout << "ViewManager::getPlaybackFrame(recording) -> " << m_playbackFrame << endl; +#endif + } else if (isPlaying()) { + m_playbackFrame = m_playSource->getCurrentPlayingFrame(); +#ifdef DEBUG_VIEW_MANAGER + cout << "ViewManager::getPlaybackFrame(playing) -> " << m_playbackFrame << endl; +#endif + } else { +#ifdef DEBUG_VIEW_MANAGER + cout << "ViewManager::getPlaybackFrame(not playing) -> " << m_playbackFrame << endl; +#endif } return m_playbackFrame; } @@ -166,13 +180,16 @@ void ViewManager::setPlaybackFrame(sv_frame_t f) { +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::setPlaybackFrame(" << f << ")" << endl; +#endif if (f < 0) f = 0; if (m_playbackFrame != f) { - m_playbackFrame = f; - emit playbackFrameChanged(f); - if (m_playSource && m_playSource->isPlaying()) { - m_playSource->play(f); - } + m_playbackFrame = f; + emit playbackFrameChanged(f); + if (isPlaying()) { + m_playSource->play(f); + } } } @@ -342,7 +359,7 @@ } ViewManager::SetSelectionCommand::SetSelectionCommand(ViewManager *vm, - const MultiSelection &ms) : + const MultiSelection &ms) : m_vm(vm), m_oldSelection(vm->m_selections), m_newSelection(ms) @@ -492,7 +509,7 @@ ViewManager::getDeviceSampleRate() const { if (m_playSource) { - return m_playSource->getDeviceSampleRate(); + return m_playSource->getDeviceSampleRate(); } return 0; } @@ -501,12 +518,21 @@ ViewManager::setAudioPlaySource(AudioPlaySource *source) { if (!m_playSource) { - QTimer::singleShot(100, this, SLOT(checkPlayStatus())); + QTimer::singleShot(100, this, SLOT(checkPlayStatus())); } m_playSource = source; } void +ViewManager::setAudioRecordTarget(AudioRecordTarget *target) +{ + if (!m_recordTarget) { + QTimer::singleShot(100, this, SLOT(checkPlayStatus())); + } + m_recordTarget = target; +} + +void ViewManager::playStatusChanged(bool /* playing */) { #ifdef DEBUG_VIEW_MANAGER @@ -516,39 +542,69 @@ } void +ViewManager::recordStatusChanged(bool /* recording */) +{ +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::recordStatusChanged" << endl; +#endif + checkPlayStatus(); +} + +void ViewManager::checkPlayStatus() { - if (m_playSource && m_playSource->isPlaying()) { + if (isRecording()) { - float left = 0, right = 0; - if (m_playSource->getOutputLevels(left, right)) { - if (left != m_lastLeft || right != m_lastRight) { - emit outputLevelsChanged(left, right); - m_lastLeft = left; - m_lastRight = right; - } - } + float left = 0, right = 0; + if (m_recordTarget->getInputLevels(left, right)) { + if (left != m_lastLeft || right != m_lastRight) { + emit monitoringLevelsChanged(left, right); + m_lastLeft = left; + m_lastRight = right; + } + } - m_playbackFrame = m_playSource->getCurrentPlayingFrame(); + m_playbackFrame = m_recordTarget->getRecordDuration(); #ifdef DEBUG_VIEW_MANAGER - cerr << "ViewManager::checkPlayStatus: Playing, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << endl; + cerr << "ViewManager::checkPlayStatus: Recording, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << endl; #endif - emit playbackFrameChanged(m_playbackFrame); + emit playbackFrameChanged(m_playbackFrame); - QTimer::singleShot(20, this, SLOT(checkPlayStatus())); + QTimer::singleShot(500, this, SLOT(checkPlayStatus())); + + } else if (isPlaying()) { + + float left = 0, right = 0; + if (m_playSource->getOutputLevels(left, right)) { + if (left != m_lastLeft || right != m_lastRight) { + emit monitoringLevelsChanged(left, right); + m_lastLeft = left; + m_lastRight = right; + } + } + + m_playbackFrame = m_playSource->getCurrentPlayingFrame(); + +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::checkPlayStatus: Playing, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << endl; +#endif + + emit playbackFrameChanged(m_playbackFrame); + + QTimer::singleShot(20, this, SLOT(checkPlayStatus())); } else { - if (m_lastLeft != 0.0 || m_lastRight != 0.0) { - emit outputLevelsChanged(0.0, 0.0); - m_lastLeft = 0.0; - m_lastRight = 0.0; - } + if (m_lastLeft != 0.0 || m_lastRight != 0.0) { + emit monitoringLevelsChanged(0.0, 0.0); + m_lastLeft = 0.0; + m_lastRight = 0.0; + } #ifdef DEBUG_VIEW_MANAGER - cerr << "ViewManager::checkPlayStatus: Not playing" << endl; + cerr << "ViewManager::checkPlayStatus: Not playing or recording" << endl; #endif } } @@ -559,6 +615,12 @@ return m_playSource && m_playSource->isPlaying(); } +bool +ViewManager::isRecording() const +{ + return m_recordTarget && m_recordTarget->isRecording(); +} + void ViewManager::viewCentreFrameChanged(sv_frame_t f, bool locked, PlaybackFollowMode mode) @@ -597,17 +659,25 @@ cerr << "ViewManager::seek(" << f << ")" << endl; #endif - if (m_playSource && m_playSource->isPlaying()) { - sv_frame_t playFrame = m_playSource->getCurrentPlayingFrame(); - sv_frame_t diff = std::max(f, playFrame) - std::min(f, playFrame); - if (diff > 20000) { - m_playbackFrame = f; - m_playSource->play(f); + if (isRecording()) { + // ignore +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::seek: Ignoring during recording" << endl; +#endif + return; + } + + if (isPlaying()) { + sv_frame_t playFrame = m_playSource->getCurrentPlayingFrame(); + sv_frame_t diff = std::max(f, playFrame) - std::min(f, playFrame); + if (diff > 20000) { + m_playbackFrame = f; + m_playSource->play(f); #ifdef DEBUG_VIEW_MANAGER - cerr << "ViewManager::considerSeek: reseeking from " << playFrame << " to " << f << endl; + cerr << "ViewManager::seek: reseeking from " << playFrame << " to " << f << endl; #endif emit playbackFrameChanged(f); - } + } } else { if (m_playbackFrame != f) { m_playbackFrame = f; @@ -629,7 +699,7 @@ //!!! emit zoomLevelChanged(); if (locked) { - m_globalZoom = z; + m_globalZoom = z; } #ifdef DEBUG_VIEW_MANAGER @@ -734,10 +804,13 @@ #endif double em = QFontMetrics(QFont()).height(); ratio = em / baseEm; + + SVDEBUG << "ViewManager::scalePixelSize: ratio is " << ratio + << " (em = " << em << ")" << endl; } int scaled = int(pixels * ratio + 0.5); -// cerr << "scaledSize: " << pixels << " -> " << scaled << " at ratio " << ratio << endl; +// SVDEBUG << "scaledSize: " << pixels << " -> " << scaled << " at ratio " << ratio << endl; if (pixels != 0 && scaled == 0) scaled = 1; return scaled; } diff -r 57d192e26331 -r 13d9b422f7fe view/ViewManager.h --- a/view/ViewManager.h Mon Dec 12 15:18:52 2016 +0000 +++ b/view/ViewManager.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _VIEW_MANAGER_H_ -#define _VIEW_MANAGER_H_ +#ifndef SV_VIEW_MANAGER_H +#define SV_VIEW_MANAGER_H #include #include @@ -29,6 +29,7 @@ #include "base/BaseTypes.h" class AudioPlaySource; +class AudioRecordTarget; class Model; enum PlaybackFollowMode { @@ -80,8 +81,10 @@ virtual ~ViewManager(); void setAudioPlaySource(AudioPlaySource *source); + void setAudioRecordTarget(AudioRecordTarget *target); bool isPlaying() const; + bool isRecording() const; sv_frame_t getGlobalCentreFrame() const; // the set method is a slot int getGlobalZoom() const; @@ -127,13 +130,13 @@ Clipboard &getClipboard() { return m_clipboard; } enum ToolMode { - NavigateMode, - SelectMode, + NavigateMode, + SelectMode, EditMode, - DrawMode, - EraseMode, - MeasureMode, - NoteEditMode //GF: Tonioni: this tool mode will be context sensitive. + DrawMode, + EraseMode, + MeasureMode, + NoteEditMode //GF: Tonioni: this tool mode will be context sensitive. }; ToolMode getToolMode() const { return m_toolMode; } void setToolMode(ToolMode mode); @@ -190,7 +193,7 @@ * display. This is relevant to hi-dpi systems that do not do * pixel doubling (i.e. Windows and Linux rather than OS/X). */ - int scalePixelSize(int pixels); + static int scalePixelSize(int pixels); enum OverlayMode { NoOverlays, @@ -216,6 +219,9 @@ bool shouldShowVerticalColourScale() const { return m_overlayMode == AllOverlays; } + bool shouldShowHorizontalValueScale() const { // for layers where x != time + return m_overlayMode != NoOverlays; + } bool shouldShowSelectionExtents() const { return m_overlayMode != NoOverlays && m_overlayMode != GlobalOverlays; } @@ -254,8 +260,8 @@ /** Emitted when the playback frame changes. */ void playbackFrameChanged(sv_frame_t frame); - /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */ - void outputLevelsChanged(float left, float right); + /** Emitted when the output or record levels change. Values in range 0.0 -> 1.0. */ + void monitoringLevelsChanged(float left, float right); /** Emitted whenever the selection has changed. */ void selectionChanged(); @@ -305,6 +311,7 @@ void setGlobalCentreFrame(sv_frame_t); void setPlaybackFrame(sv_frame_t); void playStatusChanged(bool playing); + void recordStatusChanged(bool recording); protected slots: void checkPlayStatus(); @@ -313,6 +320,8 @@ protected: AudioPlaySource *m_playSource; + AudioRecordTarget *m_recordTarget; + sv_frame_t m_globalCentreFrame; int m_globalZoom; mutable sv_frame_t m_playbackFrame; @@ -342,16 +351,16 @@ class SetSelectionCommand : public Command { public: - SetSelectionCommand(ViewManager *vm, const MultiSelection &ms); - virtual ~SetSelectionCommand(); - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const; + SetSelectionCommand(ViewManager *vm, const MultiSelection &ms); + virtual ~SetSelectionCommand(); + virtual void execute(); + virtual void unexecute(); + virtual QString getName() const; protected: - ViewManager *m_vm; - MultiSelection m_oldSelection; - MultiSelection m_newSelection; + ViewManager *m_vm; + MultiSelection m_oldSelection; + MultiSelection m_newSelection; }; OverlayMode m_overlayMode; diff -r 57d192e26331 -r 13d9b422f7fe view/ViewProxy.h --- a/view/ViewProxy.h Mon Dec 12 15:18:52 2016 +0000 +++ b/view/ViewProxy.h Mon Sep 17 13:51:31 2018 +0100 @@ -21,23 +21,23 @@ { public: ViewProxy(View *view, int scaleFactor) : - m_view(view), m_scaleFactor(scaleFactor) { } + m_view(view), m_scaleFactor(scaleFactor) { } virtual int getId() const { return m_view->getId(); } virtual sv_frame_t getStartFrame() const { - return m_view->getStartFrame(); + return m_view->getStartFrame(); } virtual sv_frame_t getCentreFrame() const { - return m_view->getCentreFrame(); + return m_view->getCentreFrame(); } virtual sv_frame_t getEndFrame() const { - return m_view->getEndFrame(); + return m_view->getEndFrame(); } virtual int getXForFrame(sv_frame_t frame) const { //!!! not actually correct, if frame lies between view's pixels - return m_scaleFactor * m_view->getXForFrame(frame); + return m_scaleFactor * m_view->getXForFrame(frame); } virtual sv_frame_t getFrameForX(int x) const { sv_frame_t f0 = m_view->getFrameForX(x / m_scaleFactor); @@ -52,43 +52,43 @@ return x / m_scaleFactor; } virtual sv_frame_t getModelsStartFrame() const { - return m_view->getModelsStartFrame(); + return m_view->getModelsStartFrame(); } virtual sv_frame_t getModelsEndFrame() const { - return m_view->getModelsEndFrame(); + return m_view->getModelsEndFrame(); } virtual double getYForFrequency(double frequency, - double minFreq, double maxFreq, + double minFreq, double maxFreq, bool logarithmic) const { - return m_scaleFactor * - m_view->getYForFrequency(frequency, minFreq, maxFreq, logarithmic); + return m_scaleFactor * + m_view->getYForFrequency(frequency, minFreq, maxFreq, logarithmic); } virtual double getFrequencyForY(double y, double minFreq, double maxFreq, - bool logarithmic) const { + bool logarithmic) const { return m_view->getFrequencyForY (y / m_scaleFactor, minFreq, maxFreq, logarithmic); } virtual int getTextLabelHeight(const Layer *layer, QPainter &paint) const { - return m_scaleFactor * m_view->getTextLabelHeight(layer, paint); + return m_scaleFactor * m_view->getTextLabelHeight(layer, paint); } virtual bool getValueExtents(QString unit, double &min, double &max, bool &log) const { - return m_view->getValueExtents(unit, min, max, log); + return m_view->getValueExtents(unit, min, max, log); } virtual int getZoomLevel() const { - int z = m_view->getZoomLevel(); -// cerr << "getZoomLevel: from " << z << " to "; - z = z / m_scaleFactor; -// cerr << z << endl; + int z = m_view->getZoomLevel(); +// cerr << "getZoomLevel: from " << z << " to "; + z = z / m_scaleFactor; +// cerr << z << endl; if (z < 1) z = 1; - return z; + return z; } virtual QRect getPaintRect() const { - QRect r = m_view->getPaintRect(); - return QRect(r.x() * m_scaleFactor, - r.y() * m_scaleFactor, - r.width() * m_scaleFactor, - r.height() * m_scaleFactor); + QRect r = m_view->getPaintRect(); + return QRect(r.x() * m_scaleFactor, + r.y() * m_scaleFactor, + r.width() * m_scaleFactor, + r.height() * m_scaleFactor); } virtual QSize getPaintSize() const { return getPaintRect().size(); @@ -100,33 +100,33 @@ return getPaintRect().height(); } virtual bool hasLightBackground() const { - return m_view->hasLightBackground(); + return m_view->hasLightBackground(); } virtual QColor getForeground() const { - return m_view->getForeground(); + return m_view->getForeground(); } virtual QColor getBackground() const { - return m_view->getBackground(); + return m_view->getBackground(); } virtual ViewManager *getViewManager() const { - return m_view->getViewManager(); + return m_view->getViewManager(); } - + virtual bool shouldIlluminateLocalFeatures(const Layer *layer, QPoint &point) const { QPoint p; - bool should = m_view->shouldIlluminateLocalFeatures(layer, p); + bool should = m_view->shouldIlluminateLocalFeatures(layer, p); point = QPoint(p.x() * m_scaleFactor, p.y() * m_scaleFactor); return should; } virtual bool shouldShowFeatureLabels() const { - return m_view->shouldShowFeatureLabels(); + return m_view->shouldShowFeatureLabels(); } virtual void drawMeasurementRect(QPainter &p, const Layer *layer, QRect rect, bool focus) const { - m_view->drawMeasurementRect(p, layer, rect, focus); + m_view->drawMeasurementRect(p, layer, rect, focus); } virtual void updatePaintRect(QRect r) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/ActivityLog.cpp --- a/widgets/ActivityLog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ActivityLog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -30,7 +30,9 @@ using std::cerr; using std::endl; -//#define PRINT_ACTIVITY 1 +#ifndef NO_PRINT_ACTIVITY +#define PRINT_ACTIVITY 1 +#endif ActivityLog::ActivityLog() : QDialog() { @@ -62,11 +64,11 @@ name = name.replace("&", ""); #ifdef PRINT_ACTIVITY - cerr << "ActivityLog: " << name; + SVDEBUG << "ActivityLog: " << name; if (name == m_prevName) { - cerr << " (duplicate)"; + SVDEBUG << " (duplicate)"; } - cerr << endl; + SVDEBUG << endl; #endif if (name == m_prevName) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/AudioDial.cpp --- a/widgets/AudioDial.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/AudioDial.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -122,23 +122,23 @@ QPainter paint; double angle = AUDIO_DIAL_MIN // offset - + (AUDIO_DIAL_RANGE * - (double(QDial::value() - QDial::minimum()) / - (double(QDial::maximum() - QDial::minimum())))); + + (AUDIO_DIAL_RANGE * + (double(QDial::value() - QDial::minimum()) / + (double(QDial::maximum() - QDial::minimum())))); int degrees = int(angle * 180.0 / M_PI); int ns = notchSize(); int numTicks = 1 + (maximum() + ns - minimum()) / ns; - + QColor knobColor(m_knobColor); if (knobColor == Qt::black) - knobColor = palette().window().color(); + knobColor = palette().window().color(); QColor meterColor(m_meterColor); if (!isEnabled()) - meterColor = palette().mid().color(); + meterColor = palette().mid().color(); else if (m_meterColor == Qt::white) - meterColor = palette().highlight().color(); + meterColor = palette().highlight().color(); int m_size = width() < height() ? width() : height(); int scale = 1; @@ -157,7 +157,7 @@ pen.setColor(knobColor); pen.setWidth(scale * 2); pen.setCapStyle(Qt::FlatCap); - + paint.setPen(pen); paint.setBrush(c); @@ -169,30 +169,30 @@ int pos = indent-1 + (width-2*indent) / 20; int darkWidth = (width-2*indent) * 3 / 4; while (darkWidth) { - c = c.light(102); - pen.setColor(c); - paint.setPen(pen); - paint.drawEllipse(pos, pos, darkWidth, darkWidth); - if (!--darkWidth) break; - paint.drawEllipse(pos, pos, darkWidth, darkWidth); - if (!--darkWidth) break; - paint.drawEllipse(pos, pos, darkWidth, darkWidth); - ++pos; --darkWidth; + c = c.light(102); + pen.setColor(c); + paint.setPen(pen); + paint.drawEllipse(pos, pos, darkWidth, darkWidth); + if (!--darkWidth) break; + paint.drawEllipse(pos, pos, darkWidth, darkWidth); + if (!--darkWidth) break; + paint.drawEllipse(pos, pos, darkWidth, darkWidth); + ++pos; --darkWidth; } // Tick notches... if ( notchesVisible() ) { -// cerr << "Notches visible" << endl; - pen.setColor(palette().dark().color()); - pen.setWidth(scale); - paint.setPen(pen); - for (int i = 0; i < numTicks; ++i) { - int div = numTicks; - if (div > 1) --div; - drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div, - width, true); - } +// cerr << "Notches visible" << endl; + pen.setColor(palette().dark().color()); + pen.setWidth(scale); + paint.setPen(pen); + for (int i = 0; i < numTicks; ++i) { + int div = numTicks; + if (div > 1) --div; + drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div, + width, true); + } } // The bright metering bit... @@ -208,7 +208,7 @@ if (arcLen == 0) arcLen = -16; paint.drawArc(indent/2, indent/2, - width-indent, width-indent, (180 + 45) * 16, arcLen); + width-indent, width-indent, (180 + 45) * 16, arcLen); paint.setBrush(Qt::NoBrush); @@ -222,13 +222,13 @@ int shadowAngle = -720; c = knobColor.dark(); for (int arc = 120; arc < 2880; arc += 240) { - pen.setColor(c); - paint.setPen(pen); - paint.drawArc(indent, indent, - 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); + pen.setColor(c); + paint.setPen(pen); + paint.drawArc(indent, indent, + 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); } // Scale shadow, omitting the bottom part... @@ -236,21 +236,21 @@ shadowAngle = 2160; c = palette().shadow().color(); for (int i = 0; i < 5; ++i) { - pen.setColor(c); - paint.setPen(pen); + pen.setColor(c); + paint.setPen(pen); int arc = i * 240 + 120; - paint.drawArc(scale/2, scale/2, - width-scale, width-scale, shadowAngle + arc, 240); - c = c.light(110); + paint.drawArc(scale/2, scale/2, + width-scale, width-scale, shadowAngle + arc, 240); + c = c.light(110); } c = palette().shadow().color(); for (int i = 0; i < 12; ++i) { - pen.setColor(c); - paint.setPen(pen); + pen.setColor(c); + paint.setPen(pen); int arc = i * 240 + 120; - paint.drawArc(scale/2, scale/2, - width-scale, width-scale, shadowAngle - arc, 240); - c = c.light(110); + paint.drawArc(scale/2, scale/2, + width-scale, width-scale, shadowAngle - arc, 240); + c = c.light(110); } // Scale ends... @@ -259,11 +259,11 @@ pen.setWidth(scale); paint.setPen(pen); for (int i = 0; i < numTicks; ++i) { - if (i != 0 && i != numTicks - 1) continue; - int div = numTicks; - if (div > 1) --div; - drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div, - width, false); + if (i != 0 && i != numTicks - 1) continue; + int div = numTicks; + if (div > 1) --div; + drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div, + width, false); } // Pointer notch... @@ -289,7 +289,7 @@ void AudioDial::drawTick(QPainter &paint, - double angle, int size, bool internal) + double angle, int size, bool internal) { double hyp = double(size) / 2.0; double x0 = hyp - (hyp - 1) * sin(angle); @@ -299,19 +299,19 @@ if (internal) { - double len = hyp / 4; - double x1 = hyp - (hyp - len) * sin(angle); - double y1 = hyp + (hyp - len) * cos(angle); - - paint.drawLine(int(x0), int(y0), int(x1), int(y1)); + double len = hyp / 4; + double x1 = hyp - (hyp - len) * sin(angle); + double y1 = hyp + (hyp - len) * cos(angle); + + paint.drawLine(int(x0), int(y0), int(x1), int(y1)); } else { - double len = hyp / 4; - double x1 = hyp - (hyp + len) * sin(angle); - double y1 = hyp + (hyp + len) * cos(angle); + double len = hyp / 4; + double x1 = hyp - (hyp + len) * sin(angle); + double y1 = hyp + (hyp + len) * cos(angle); - paint.drawLine(int(x0), int(y0), int(x1), int(y1)); + paint.drawLine(int(x0), int(y0), int(x1), int(y1)); } } @@ -452,14 +452,14 @@ void AudioDial::mousePressEvent(QMouseEvent *mouseEvent) { if (m_mouseDial) { - QDial::mousePressEvent(mouseEvent); + QDial::mousePressEvent(mouseEvent); } else if (mouseEvent->button() == Qt::MidButton || ((mouseEvent->button() == Qt::LeftButton) && (mouseEvent->modifiers() & Qt::ControlModifier))) { setToDefault(); } else if (mouseEvent->button() == Qt::LeftButton) { - m_mousePressed = true; - m_posMouse = mouseEvent->pos(); + m_mousePressed = true; + m_posMouse = mouseEvent->pos(); } } @@ -469,7 +469,7 @@ //!!! needs a common base class with Thumbwheel if (m_mouseDial) { - QDial::mouseDoubleClickEvent(mouseEvent); + QDial::mouseDoubleClickEvent(mouseEvent); } else if (mouseEvent->button() != Qt::LeftButton) { return; } @@ -541,19 +541,19 @@ void AudioDial::mouseMoveEvent(QMouseEvent *mouseEvent) { if (m_mouseDial) { - QDial::mouseMoveEvent(mouseEvent); + QDial::mouseMoveEvent(mouseEvent); } else if (m_mousePressed) { - const QPoint& posMouse = mouseEvent->pos(); - int v = QDial::value() - + (posMouse.x() - m_posMouse.x()) - + (m_posMouse.y() - posMouse.y()); - if (v > QDial::maximum()) - v = QDial::maximum(); - else - if (v < QDial::minimum()) - v = QDial::minimum(); - m_posMouse = posMouse; - QDial::setValue(v); + const QPoint& posMouse = mouseEvent->pos(); + int v = QDial::value() + + (posMouse.x() - m_posMouse.x()) + + (m_posMouse.y() - posMouse.y()); + if (v > QDial::maximum()) + v = QDial::maximum(); + else + if (v < QDial::minimum()) + v = QDial::minimum(); + m_posMouse = posMouse; + QDial::setValue(v); } } @@ -561,9 +561,9 @@ void AudioDial::mouseReleaseEvent(QMouseEvent *mouseEvent) { if (m_mouseDial) { - QDial::mouseReleaseEvent(mouseEvent); + QDial::mouseReleaseEvent(mouseEvent); } else if (m_mousePressed) { - m_mousePressed = false; + m_mousePressed = false; } } diff -r 57d192e26331 -r 13d9b422f7fe widgets/CSVAudioFormatDialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CSVAudioFormatDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,220 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "CSVAudioFormatDialog.h" + +#include "layer/LayerFactory.h" + +#include "TextAbbrev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/Debug.h" + +CSVAudioFormatDialog::CSVAudioFormatDialog(QWidget *parent, CSVFormat format, + int maxDisplayCols) : + QDialog(parent), + m_format(format), + m_maxDisplayCols(maxDisplayCols), + m_fuzzyColumn(-1) +{ + setModal(true); + setWindowTitle(tr("Select Audio Data Format")); + + QGridLayout *layout = new QGridLayout; + + int row = 0; + + layout->addWidget + (new QLabel(tr("Please select the correct data format for this file.")), + row++, 0, 1, 4); + + QFrame *exampleFrame = new QFrame; + exampleFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + exampleFrame->setLineWidth(2); + QGridLayout *exampleLayout = new QGridLayout; + exampleLayout->setSpacing(4); + exampleFrame->setLayout(exampleLayout); + + QPalette palette = exampleFrame->palette(); + palette.setColor(QPalette::Window, palette.color(QPalette::Base)); + exampleFrame->setPalette(palette); + + QFont fp; + fp.setPointSize(int(floor(fp.pointSize() * 0.9))); + + int columns = format.getColumnCount(); + QList example = m_format.getExample(); + + for (int i = 0; i < columns; ++i) { + + QComboBox *cpc = new QComboBox; + m_columnPurposeCombos.push_back(cpc); + exampleLayout->addWidget(cpc, 0, i); + connect(cpc, SIGNAL(activated(int)), this, SLOT(columnPurposeChanged(int))); + + if (i == m_maxDisplayCols && columns > i + 2) { + m_fuzzyColumn = i; + + cpc->addItem(tr("")); + cpc->addItem(tr("Audio channels")); + cpc->setCurrentIndex + (m_format.getColumnPurpose(i-1) == CSVFormat::ColumnValue ? + 1 : 0); + + exampleLayout->addWidget + (new QLabel(tr("(%1 more)").arg(columns - i)), 1, i); + break; + } + + cpc->addItem(tr("")); + cpc->addItem(tr("Audio channel")); + cpc->setCurrentIndex + (m_format.getColumnPurpose(i) == CSVFormat::ColumnValue ? 1 : 0); + + for (int j = 0; j < example.size() && j < 6; ++j) { + if (i >= example[j].size()) { + continue; + } + QLabel *label = new QLabel; + label->setTextFormat(Qt::PlainText); + QString text = TextAbbrev::abbreviate(example[j][i], 35); + label->setText(text); + label->setFont(fp); + label->setPalette(palette); + label->setIndent(8); + exampleLayout->addWidget(label, j+1, i); + } + } + + layout->addWidget(exampleFrame, row, 0, 1, 4); + layout->setColumnStretch(3, 10); + layout->setRowStretch(row++, 10); + + layout->addWidget(new QLabel(tr("Audio sample rate (Hz):")), row, 0); + + int sampleRates[] = { + 8000, 11025, 12000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 + }; + + m_sampleRateCombo = new QComboBox; + for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) { + m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); + if (sampleRates[i] == m_format.getSampleRate()) { + m_sampleRateCombo->setCurrentIndex(i); + } + } + m_sampleRateCombo->setEditable(true); + + layout->addWidget(m_sampleRateCombo, row++, 1); + connect(m_sampleRateCombo, SIGNAL(activated(QString)), + this, SLOT(sampleRateChanged(QString))); + connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)), + this, SLOT(sampleRateChanged(QString))); + + layout->addWidget(new QLabel(tr("Sample values are:")), row, 0); + + m_sampleRangeCombo = new QComboBox; + // NB must be in the same order as the CSVFormat::AudioSampleRange enum + m_sampleRangeCombo->addItem(tr("Floating-point in range -1 to 1")); + m_sampleRangeCombo->addItem(tr("8-bit in range 0 to 255")); + m_sampleRangeCombo->addItem(tr("16-bit in range -32768 to 32767")); + m_sampleRangeCombo->addItem(tr("Unknown range: normalise on load")); + m_sampleRangeCombo->setCurrentIndex(int(m_format.getAudioSampleRange())); + + layout->addWidget(m_sampleRangeCombo, row++, 1); + connect(m_sampleRangeCombo, SIGNAL(activated(int)), + this, SLOT(sampleRangeChanged(int))); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok | + QDialogButtonBox::Cancel); + layout->addWidget(bb, row++, 0, 1, 4); + connect(bb, SIGNAL(accepted()), this, SLOT(accept())); + connect(bb, SIGNAL(rejected()), this, SLOT(reject())); + + setLayout(layout); + + updateFormatFromDialog(); +} + +CSVAudioFormatDialog::~CSVAudioFormatDialog() +{ +} + +CSVFormat +CSVAudioFormatDialog::getFormat() const +{ + return m_format; +} + +void +CSVAudioFormatDialog::sampleRateChanged(QString rateString) +{ + bool ok = false; + int sampleRate = rateString.toInt(&ok); + if (ok) m_format.setSampleRate(sampleRate); +} + +void +CSVAudioFormatDialog::sampleRangeChanged(int range) +{ + m_format.setAudioSampleRange((CSVFormat::AudioSampleRange)range); +} + +void +CSVAudioFormatDialog::columnPurposeChanged(int) +{ + updateFormatFromDialog(); +} + +void +CSVAudioFormatDialog::updateFormatFromDialog() +{ + m_format.setModelType(CSVFormat::WaveFileModel); + m_format.setTimingType(CSVFormat::ImplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeAudioFrames); + + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { + + QComboBox *thisCombo = m_columnPurposeCombos[i]; + + CSVFormat::ColumnPurpose purpose = (thisCombo->currentIndex() == 1 ? + CSVFormat::ColumnValue : + CSVFormat::ColumnUnknown); + + if (i == m_fuzzyColumn) { + for (int j = i; j < m_format.getColumnCount(); ++j) { + m_format.setColumnPurpose(j, purpose); + } + } else { + m_format.setColumnPurpose(i, purpose); + } + } +} + + + diff -r 57d192e26331 -r 13d9b422f7fe widgets/CSVAudioFormatDialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CSVAudioFormatDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,57 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_CSV_AUDIO_FORMAT_DIALOG_H +#define SV_CSV_AUDIO_FORMAT_DIALOG_H + +#include "data/fileio/CSVFormat.h" + +class QTableWidget; +class QComboBox; +class QLabel; + +#include + +class CSVAudioFormatDialog : public QDialog +{ + Q_OBJECT + +public: + CSVAudioFormatDialog(QWidget *parent, + CSVFormat initialFormat, + int maxDisplayCols = 5); + ~CSVAudioFormatDialog(); + + CSVFormat getFormat() const; + +protected slots: + void sampleRateChanged(QString); + void sampleRangeChanged(int); + void columnPurposeChanged(int purpose); + + void updateFormatFromDialog(); + +protected: + CSVFormat m_format; + int m_maxDisplayCols; + + QComboBox *m_sampleRateCombo; + QComboBox *m_sampleRangeCombo; + + QList m_columnPurposeCombos; + int m_fuzzyColumn; +}; + +#endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/CSVFormatDialog.cpp --- a/widgets/CSVFormatDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/CSVFormatDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -48,8 +48,9 @@ int row = 0; - layout->addWidget(new QLabel(tr("Please select the correct data format for this file.")), - row++, 0, 1, 4); + layout->addWidget + (new QLabel(tr("Please select the correct data format for this file.")), + row++, 0, 1, 4); QFrame *exampleFrame = new QFrame; exampleFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); @@ -64,9 +65,6 @@ QFont fp; fp.setPointSize(int(floor(fp.pointSize() * 0.9))); -// fp.setFixedPitch(true); -// fp.setStyleHint(QFont::TypeWriter); -// fp.setFamily("Monospaced"); int columns = format.getColumnCount(); QList example = m_format.getExample(); @@ -77,15 +75,18 @@ m_columnPurposeCombos.push_back(cpc); exampleLayout->addWidget(cpc, 0, i); connect(cpc, SIGNAL(activated(int)), this, SLOT(columnPurposeChanged(int))); - + if (i == m_maxDisplayCols && columns > i + 2) { m_fuzzyColumn = i; + cpc->addItem(tr("")); cpc->addItem(tr("Values")); cpc->setCurrentIndex - (m_format.getColumnPurpose(i-1) == CSVFormat::ColumnUnknown ? 0 : 1); - exampleLayout->addWidget(new QLabel(tr("(%1 more)").arg(columns - i)), - 1, i); + (m_format.getColumnPurpose(i-1) == + CSVFormat::ColumnUnknown ? 0 : 1); + + exampleLayout->addWidget + (new QLabel(tr("(%1 more)").arg(columns - i)), 1, i); break; } @@ -98,7 +99,7 @@ cpc->addItem(tr("Pitch")); // ColumnPitch cpc->addItem(tr("Label")); // ColumnLabel cpc->setCurrentIndex(int(m_format.getColumnPurpose(i))); - + for (int j = 0; j < example.size() && j < 6; ++j) { if (i >= example[j].size()) { continue; @@ -132,12 +133,12 @@ for (auto &l: m_timingLabels) { m_timingTypeCombo->addItem(l.second); } - + layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2); - + connect(m_timingTypeCombo, SIGNAL(activated(int)), - this, SLOT(timingTypeChanged(int))); - + this, SLOT(timingTypeChanged(int))); + m_initialTimingOption = TimingImplicit; if (m_format.getTimingType() == CSVFormat::ExplicitTiming) { switch (m_format.getTimeUnits()) { @@ -152,19 +153,19 @@ } } m_timingTypeCombo->setCurrentIndex(int(m_initialTimingOption)); - + m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):")); layout->addWidget(m_sampleRateLabel, row, 0); int sampleRates[] = { - 8000, 11025, 12000, 22050, 24000, 32000, - 44100, 48000, 88200, 96000, 176400, 192000 + 8000, 11025, 12000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 }; m_sampleRateCombo = new QComboBox; for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) { - m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); - if (sampleRates[i] == m_format.getSampleRate()) { + m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); + if (sampleRates[i] == m_format.getSampleRate()) { m_sampleRateCombo->setCurrentIndex(i); } } @@ -172,28 +173,28 @@ layout->addWidget(m_sampleRateCombo, row++, 1); connect(m_sampleRateCombo, SIGNAL(activated(QString)), - this, SLOT(sampleRateChanged(QString))); + this, SLOT(sampleRateChanged(QString))); connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)), - this, SLOT(sampleRateChanged(QString))); + this, SLOT(sampleRateChanged(QString))); m_windowSizeLabel = new QLabel(tr("Frame increment between rows:")); layout->addWidget(m_windowSizeLabel, row, 0); - + m_windowSizeCombo = new QComboBox; for (int i = 0; i <= 16; ++i) { - int value = 1 << i; - m_windowSizeCombo->addItem(QString("%1").arg(value)); - if (value == int(m_format.getWindowSize())) { + int value = 1 << i; + m_windowSizeCombo->addItem(QString("%1").arg(value)); + if (value == int(m_format.getWindowSize())) { m_windowSizeCombo->setCurrentIndex(i); } } m_windowSizeCombo->setEditable(true); - + layout->addWidget(m_windowSizeCombo, row++, 1); connect(m_windowSizeCombo, SIGNAL(activated(QString)), - this, SLOT(windowSizeChanged(QString))); + this, SLOT(windowSizeChanged(QString))); connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)), - this, SLOT(windowSizeChanged(QString))); + this, SLOT(windowSizeChanged(QString))); m_modelLabel = new QLabel; QFont f(m_modelLabel->font()); @@ -225,6 +226,10 @@ void CSVFormatDialog::updateModelLabel() { + if (!m_modelLabel) { + return; + } + LayerFactory *f = LayerFactory::getInstance(); QString s; @@ -244,9 +249,13 @@ case CSVFormat::ThreeDimensionalModel: s = f->getLayerPresentationName(LayerFactory::Colour3DPlot); break; + case CSVFormat::WaveFileModel: + s = f->getLayerPresentationName(LayerFactory::Waveform); + break; } - m_modelLabel->setText("\n" + tr("Data will be displayed in a %1 layer.").arg(s)); + m_modelLabel->setText("\n" + tr("Data will be displayed in a %1 layer.") + .arg(s)); } void @@ -341,6 +350,11 @@ QComboBox *cb = qobject_cast(o); if (!cb) return; + // Ensure a consistent set of column purposes, in case of a + // situation where some combinations are contradictory. Only + // updates the UI, does not update the stored format record from + // the UI - that's the job of updateFormatFromDialog + CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p; bool haveStartTime = false; // so as to update timing type combo appropriately @@ -412,33 +426,33 @@ updateFormatFromDialog(); updateComboVisibility(); } - + void CSVFormatDialog::updateFormatFromDialog() { switch (TimingOption(m_timingTypeCombo->currentIndex())) { case TimingExplicitSeconds: - m_format.setTimingType(CSVFormat::ExplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeSeconds); - break; - + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeSeconds); + break; + case TimingExplicitMsec: - m_format.setTimingType(CSVFormat::ExplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeMilliseconds); - break; - + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeMilliseconds); + break; + case TimingExplicitSamples: - m_format.setTimingType(CSVFormat::ExplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeAudioFrames); - break; - + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeAudioFrames); + break; + case TimingImplicit: - m_format.setTimingType(CSVFormat::ImplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeWindows); - break; + m_format.setTimingType(CSVFormat::ImplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeWindows); + break; } - + bool haveStartTime = false; bool haveDuration = false; bool havePitch = false; @@ -448,9 +462,9 @@ QComboBox *thisCombo = m_columnPurposeCombos[i]; - CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose) - (thisCombo->currentIndex()); - + CSVFormat::ColumnPurpose purpose = + (CSVFormat::ColumnPurpose) (thisCombo->currentIndex()); + if (i == m_fuzzyColumn) { for (int j = i; j < m_format.getColumnCount(); ++j) { if (purpose == CSVFormat::ColumnUnknown) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/CSVFormatDialog.h --- a/widgets/CSVFormatDialog.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/CSVFormatDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _CSV_FORMAT_DIALOG_H_ -#define _CSV_FORMAT_DIALOG_H_ +#ifndef SV_CSV_FORMAT_DIALOG_H +#define SV_CSV_FORMAT_DIALOG_H #include "data/fileio/CSVFormat.h" @@ -29,7 +29,8 @@ Q_OBJECT public: - CSVFormatDialog(QWidget *parent, CSVFormat initialFormat, + CSVFormatDialog(QWidget *parent, + CSVFormat initialFormat, int maxDisplayCols = 5); ~CSVFormatDialog(); @@ -47,7 +48,7 @@ protected: CSVFormat m_format; int m_maxDisplayCols; - + enum TimingOption { TimingExplicitSeconds = 0, TimingExplicitMsec, @@ -57,6 +58,7 @@ std::map m_timingLabels; TimingOption m_initialTimingOption; + void columnPurposeChangedForAnnotationType(QComboBox *, int purpose); void updateComboVisibility(); void applyStartTimePurpose(); void removeStartTimePurpose(); diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourComboBox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ColourComboBox.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,101 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007-2016 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ColourComboBox.h" + +#include "ColourNameDialog.h" + +#include "layer/ColourDatabase.h" + +#include "base/Debug.h" + +#include +#include + +#include + +using namespace std; + +ColourComboBox::ColourComboBox(bool withAddNewColourEntry, QWidget *parent) : + NotifyingComboBox(parent), + m_withAddNewColourEntry(withAddNewColourEntry) +{ + setEditable(false); + rebuild(); + + connect(this, SIGNAL(activated(int)), this, SLOT(comboActivated(int))); + connect(ColourDatabase::getInstance(), SIGNAL(colourDatabaseChanged()), + this, SLOT(rebuild())); + + if (count() < 20 && count() > maxVisibleItems()) { + setMaxVisibleItems(count()); + } +} + +void +ColourComboBox::comboActivated(int index) +{ + if (!m_withAddNewColourEntry || + index < int(ColourDatabase::getInstance()->getColourCount())) { + emit colourChanged(index); + return; + } + + QColor newColour = QColorDialog::getColor(); + if (!newColour.isValid()) return; + + ColourNameDialog dialog(tr("Name New Colour"), + tr("Enter a name for the new colour:"), + newColour, newColour.name(), this); + dialog.showDarkBackgroundCheckbox(tr("Prefer black background for this colour")); + if (dialog.exec() == QDialog::Accepted) { + //!!! command + ColourDatabase *db = ColourDatabase::getInstance(); + int index = db->addColour(newColour, dialog.getColourName()); + db->setUseDarkBackground(index, dialog.isDarkBackgroundChecked()); + // addColour will have called back on rebuild(), and the new + // colour will be at the index previously occupied by Add New + // Colour, which is our current index + emit colourChanged(currentIndex()); + } +} + +void +ColourComboBox::rebuild() +{ + blockSignals(true); + + int ix = currentIndex(); + + clear(); + + int size = (QFontMetrics(QFont()).height() * 2) / 3; + if (size < 12) size = 12; + + ColourDatabase *db = ColourDatabase::getInstance(); + for (int i = 0; i < db->getColourCount(); ++i) { + QString name = db->getColourName(i); + addItem(db->getExamplePixmap(i, QSize(size, size)), name); + } + + if (m_withAddNewColourEntry) { + addItem(tr("Add New Colour...")); + } + + setCurrentIndex(ix); + + blockSignals(false); +} + diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourComboBox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ColourComboBox.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,44 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007-2016 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_COLOUR_COMBO_BOX_H +#define SV_COLOUR_COMBO_BOX_H + +#include "NotifyingComboBox.h" + +/** + * Colour-picker combo box with swatches, optionally including "Add + * New Colour..." entry to invoke a QColorDialog/ColourNameDialog + */ +class ColourComboBox : public NotifyingComboBox +{ + Q_OBJECT + +public: + ColourComboBox(bool withAddNewColourEntry, QWidget *parent = 0); + +signals: + void colourChanged(int colourIndex); + +private slots: + void rebuild(); + void comboActivated(int); + +private: + bool m_withAddNewColourEntry; +}; + +#endif + diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourMapComboBox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ColourMapComboBox.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,74 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007-2016 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ColourMapComboBox.h" + +#include "layer/ColourMapper.h" + +#include "base/Debug.h" + +#include + +#include + +using namespace std; + +ColourMapComboBox::ColourMapComboBox(bool includeSwatches, QWidget *parent) : + NotifyingComboBox(parent), + m_includeSwatches(includeSwatches) +{ + setEditable(false); + rebuild(); + + connect(this, SIGNAL(activated(int)), this, SLOT(comboActivated(int))); + + if (count() < 20 && count() > maxVisibleItems()) { + setMaxVisibleItems(count()); + } +} + +void +ColourMapComboBox::comboActivated(int index) +{ + emit colourMapChanged(index); +} + +void +ColourMapComboBox::rebuild() +{ + blockSignals(true); + + int ix = currentIndex(); + + clear(); + + int size = (QFontMetrics(QFont()).height() * 2) / 3; + if (size < 12) size = 12; + + for (int i = 0; i < ColourMapper::getColourMapCount(); ++i) { + QString name = ColourMapper::getColourMapName(i); + if (m_includeSwatches) { + ColourMapper mapper(i, 0.0, 1.0); + addItem(mapper.getExamplePixmap(QSize(size * 2, size)), name); + } else { + addItem(name); + } + } + + setCurrentIndex(ix); + + blockSignals(false); +} + diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourMapComboBox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ColourMapComboBox.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,43 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007-2016 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_COLOURMAP_COMBO_BOX_H +#define SV_COLOURMAP_COMBO_BOX_H + +#include "NotifyingComboBox.h" + +/** + * Colour map picker combo box with optional swatches + */ +class ColourMapComboBox : public NotifyingComboBox +{ + Q_OBJECT + +public: + ColourMapComboBox(bool includeSwatches, QWidget *parent = 0); + +signals: + void colourMapChanged(int index); + +private slots: + void rebuild(); + void comboActivated(int); + +private: + bool m_includeSwatches; +}; + +#endif + diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourNameDialog.cpp --- a/widgets/ColourNameDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ColourNameDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -25,8 +25,8 @@ #include ColourNameDialog::ColourNameDialog(QString title, QString message, - QColor colour, QString defaultName, - QWidget *parent) : + QColor colour, QString defaultName, + QWidget *parent) : QDialog(parent), m_colour(colour) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/ColourNameDialog.h --- a/widgets/ColourNameDialog.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ColourNameDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -31,8 +31,8 @@ public: ColourNameDialog(QString title, QString message, QColor colour, - QString defaultName, - QWidget *parent = 0); + QString defaultName, + QWidget *parent = 0); void showDarkBackgroundCheckbox(QString text); diff -r 57d192e26331 -r 13d9b422f7fe widgets/CommandHistory.cpp --- a/widgets/CommandHistory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/CommandHistory.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -70,7 +70,7 @@ m_undoMenu = new QMenu(tr("&Undo")); m_undoMenuAction->setMenu(m_undoMenu); connect(m_undoMenu, SIGNAL(triggered(QAction *)), - this, SLOT(undoActivated(QAction*))); + this, SLOT(undoActivated(QAction*))); m_redoAction = new QAction(redoIcon, tr("Re&do"), this); m_redoAction->setShortcut(tr("Ctrl+Shift+Z")); @@ -83,7 +83,7 @@ m_redoMenu = new QMenu(tr("Re&do")); m_redoMenuAction->setMenu(m_redoMenu); connect(m_redoMenu, SIGNAL(triggered(QAction *)), - this, SLOT(redoActivated(QAction*))); + this, SLOT(redoActivated(QAction*))); } CommandHistory::~CommandHistory() @@ -136,8 +136,8 @@ if (!command) return; if (m_currentCompound) { - addToCompound(command, m_executeCompound); - return; + addToCompound(command, m_executeCompound); + return; } addCommand(command, true); @@ -153,15 +153,15 @@ #endif if (m_currentCompound) { - addToCompound(command, execute); - return; + addToCompound(command, execute); + return; } if (bundle) { - addToBundle(command, execute); - return; + addToBundle(command, execute); + return; } else if (m_currentBundle) { - closeBundle(); + closeBundle(); } #ifdef DEBUG_COMMAND_HISTORY @@ -180,7 +180,7 @@ clipCommands(); if (execute) { - command->execute(); + command->execute(); } // Emit even if we aren't executing the command, because @@ -196,13 +196,13 @@ CommandHistory::addToBundle(Command *command, bool execute) { if (m_currentBundle) { - if (!command || (command->getName() != m_currentBundleName)) { + if (!command || (command->getName() != m_currentBundleName)) { #ifdef DEBUG_COMMAND_HISTORY cerr << "CommandHistory::addToBundle: " << command->getName() << ": closing current bundle" << endl; #endif - closeBundle(); - } + closeBundle(); + } } if (!command) return; @@ -214,14 +214,14 @@ << ": creating new bundle" << endl; #endif - // need to addCommand before setting m_currentBundle, as addCommand - // with bundle false will reset m_currentBundle to 0 - MacroCommand *mc = new BundleCommand(command->getName()); + // need to addCommand before setting m_currentBundle, as addCommand + // with bundle false will reset m_currentBundle to 0 + MacroCommand *mc = new BundleCommand(command->getName()); m_bundling = true; - addCommand(mc, false); + addCommand(mc, false); m_bundling = false; - m_currentBundle = mc; - m_currentBundleName = command->getName(); + m_currentBundle = mc; + m_currentBundleName = command->getName(); } #ifdef DEBUG_COMMAND_HISTORY @@ -272,7 +272,7 @@ CommandHistory::addToCompound(Command *command, bool execute) { if (!m_currentCompound) { - cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; return; } @@ -288,8 +288,8 @@ CommandHistory::startCompoundOperation(QString name, bool execute) { if (m_currentCompound) { - cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; - cerr << "(name is " << m_currentCompound->getName() << ")" << endl; + cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; + cerr << "(name is " << m_currentCompound->getName() << ")" << endl; return; } @@ -307,7 +307,7 @@ CommandHistory::endCompoundOperation() { if (!m_currentCompound) { - cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; return; } @@ -433,7 +433,7 @@ CommandHistory::clipCommands() { if (int(m_undoStack.size()) > m_undoLimit) { - m_savedAt -= (int(m_undoStack.size()) - m_undoLimit); + m_savedAt -= (int(m_undoStack.size()) - m_undoLimit); } clipStack(m_undoStack, m_undoLimit); @@ -447,23 +447,23 @@ if ((int)stack.size() > limit) { - CommandStack tempStack; + CommandStack tempStack; - for (i = 0; i < limit; ++i) { + for (i = 0; i < limit; ++i) { #ifdef DEBUG_COMMAND_HISTORY - Command *command = stack.top(); - cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; + Command *command = stack.top(); + cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; #endif - tempStack.push(stack.top()); - stack.pop(); - } + tempStack.push(stack.top()); + stack.pop(); + } - clearStack(stack); + clearStack(stack); - for (i = 0; i < m_undoLimit; ++i) { - stack.push(tempStack.top()); - tempStack.pop(); - } + for (i = 0; i < m_undoLimit; ++i) { + stack.push(tempStack.top()); + tempStack.pop(); + } } } @@ -471,13 +471,13 @@ CommandHistory::clearStack(CommandStack &stack) { while (!stack.empty()) { - Command *command = stack.top(); - // Not safe to call getName() on a command about to be deleted + Command *command = stack.top(); + // Not safe to call getName() on a command about to be deleted #ifdef DEBUG_COMMAND_HISTORY - cerr << "CommandHistory::clearStack: About to delete command " << command << endl; + cerr << "CommandHistory::clearStack: About to delete command " << command << endl; #endif - delete command; - stack.pop(); + delete command; + stack.pop(); } } @@ -486,7 +486,7 @@ { int pos = m_actionCounts[action]; for (int i = 0; i <= pos; ++i) { - undo(); + undo(); } } @@ -495,7 +495,7 @@ { int pos = m_actionCounts[action]; for (int i = 0; i <= pos; ++i) { - redo(); + redo(); } } @@ -506,62 +506,62 @@ for (int undo = 0; undo <= 1; ++undo) { - QAction *action(undo ? m_undoAction : m_redoAction); - QAction *menuAction(undo ? m_undoMenuAction : m_redoMenuAction); - QMenu *menu(undo ? m_undoMenu : m_redoMenu); - CommandStack &stack(undo ? m_undoStack : m_redoStack); + QAction *action(undo ? m_undoAction : m_redoAction); + QAction *menuAction(undo ? m_undoMenuAction : m_redoMenuAction); + QMenu *menu(undo ? m_undoMenu : m_redoMenu); + CommandStack &stack(undo ? m_undoStack : m_redoStack); - if (stack.empty()) { + if (stack.empty()) { - QString text(undo ? tr("Nothing to undo") : tr("Nothing to redo")); + QString text(undo ? tr("Nothing to undo") : tr("Nothing to redo")); - action->setEnabled(false); - action->setText(text); + action->setEnabled(false); + action->setText(text); - menuAction->setEnabled(false); - menuAction->setText(text); + menuAction->setEnabled(false); + menuAction->setText(text); - } else { + } else { - action->setEnabled(true); - menuAction->setEnabled(true); + action->setEnabled(true); + menuAction->setEnabled(true); - QString commandName = stack.top()->getName(); - commandName.replace(QRegExp("&"), ""); + QString commandName = stack.top()->getName(); + commandName.replace(QRegExp("&"), ""); - QString text = (undo ? tr("&Undo %1") : tr("Re&do %1")) - .arg(commandName); + QString text = (undo ? tr("&Undo %1") : tr("Re&do %1")) + .arg(commandName); - action->setText(text); - menuAction->setText(text); - } + action->setText(text); + menuAction->setText(text); + } - menu->clear(); + menu->clear(); - CommandStack tempStack; - int j = 0; + CommandStack tempStack; + int j = 0; - while (j < m_menuLimit && !stack.empty()) { + while (j < m_menuLimit && !stack.empty()) { - Command *command = stack.top(); - tempStack.push(command); - stack.pop(); + Command *command = stack.top(); + tempStack.push(command); + stack.pop(); - QString commandName = command->getName(); - commandName.replace(QRegExp("&"), ""); + QString commandName = command->getName(); + commandName.replace(QRegExp("&"), ""); - QString text; - if (undo) text = tr("&Undo %1").arg(commandName); - else text = tr("Re&do %1").arg(commandName); - - QAction *action = menu->addAction(text); - m_actionCounts[action] = j++; - } + QString text; + if (undo) text = tr("&Undo %1").arg(commandName); + else text = tr("Re&do %1").arg(commandName); + + QAction *action = menu->addAction(text); + m_actionCounts[action] = j++; + } - while (!tempStack.empty()) { - stack.push(tempStack.top()); - tempStack.pop(); - } + while (!tempStack.empty()) { + stack.push(tempStack.top()); + tempStack.pop(); + } } } diff -r 57d192e26331 -r 13d9b422f7fe widgets/Fader.cpp --- a/widgets/Fader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/Fader.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -59,25 +59,25 @@ QString background_path = ":/icons/fader_background.png"; bool ok = m_back.load(background_path); if (ok == false) { - cerr << "Fader: Error loading pixmap" << endl; + cerr << "Fader: Error loading pixmap" << endl; } QString leds_path = ":/icons/fader_leds.png"; ok = m_leds.load(leds_path); if (ok == false) { - cerr << "Error loading pixmap" << endl; + cerr << "Error loading pixmap" << endl; } QString knob_path = ":/icons/fader_knob.png"; ok = m_knob.load(knob_path); if (ok == false) { - cerr << "Error loading pixmap" << endl; + cerr << "Error loading pixmap" << endl; } QString clip_path = ":/icons/fader_knob_red.png"; ok = m_clip.load(clip_path); if (ok == false) { - cerr << "Error loading pixmap" << endl; + cerr << "Error loading pixmap" << endl; } } @@ -111,7 +111,7 @@ if (vx < 0) vx = 0; float fval = (float)AudioLevel::fader_to_multiplier - (vx, getMaxX(), AudioLevel::LongFader); + (vx, getMaxX(), AudioLevel::LongFader); setValue(fval); emit valueChanged(fval); @@ -179,9 +179,9 @@ //!!! needs improvement if (ev->delta() > 0) { - setValue(m_value * 1.f); + setValue(m_value * 1.f); } else { - setValue(m_value / 1.f); + setValue(m_value / 1.f); } update(); @@ -206,27 +206,27 @@ float max = (float)AudioLevel::dB_to_multiplier(10.0); if (v > max) { - v = max; + v = max; } else if (v < 0.0) { - v = 0.0; + v = 0.0; } if (m_value != v) { - m_value = v; - float db = (float)AudioLevel::multiplier_to_dB(m_value); + m_value = v; + float db = (float)AudioLevel::multiplier_to_dB(m_value); QString text; - if (db <= AudioLevel::DB_FLOOR) { + if (db <= AudioLevel::DB_FLOOR) { text = tr("Level: Off"); - } else { + } else { text = tr("Level: %1%2.%3%4 dB") .arg(db < 0.0 ? "-" : "") .arg(abs(int(db))) .arg(abs(int(db * 10.0) % 10)) .arg(abs(int(db * 100.0) % 10)); - } + } cerr << "Fader: setting tooltip to \"" << text << "\"" << endl; QWidget::setToolTip(text); - update(); + update(); } } @@ -243,8 +243,8 @@ Fader::setPeakLeft(float peak) { if (this->m_peakLeft != peak) { - this->m_peakLeft = peak; - update(); + this->m_peakLeft = peak; + update(); } } @@ -253,8 +253,8 @@ Fader::setPeakRight(float peak) { if (this->m_peakRight != peak) { - this->m_peakRight = peak; - update(); + this->m_peakRight = peak; + update(); } } @@ -268,30 +268,30 @@ painter.drawPixmap(rect(), m_back, QRect(0, 0, 116, 23)); int offset_L = AudioLevel::multiplier_to_fader(m_peakLeft, 116, - AudioLevel::IEC268LongMeter); + AudioLevel::IEC268LongMeter); painter.drawPixmap(QRect(0, 0, offset_L, 11), m_leds, - QRect(0, 0, offset_L, 11)); + QRect(0, 0, offset_L, 11)); int offset_R = AudioLevel::multiplier_to_fader(m_peakRight, 116, - AudioLevel::IEC268LongMeter); + AudioLevel::IEC268LongMeter); painter.drawPixmap(QRect(0, 11, offset_R, 11), m_leds, - QRect(0, 11, offset_R, 11)); + QRect(0, 11, offset_R, 11)); if (m_withoutKnob == false) { - static const uint knob_width = 29; - static const uint knob_height = 9; + static const uint knob_width = 29; + static const uint knob_height = 9; - int x = AudioLevel::multiplier_to_fader(m_value, 116 - knob_width, - AudioLevel::LongFader); + int x = AudioLevel::multiplier_to_fader(m_value, 116 - knob_width, + AudioLevel::LongFader); - bool clipping = (m_peakLeft > 1.0 || m_peakRight > 1.0); + bool clipping = (m_peakLeft > 1.0 || m_peakRight > 1.0); - painter.drawPixmap(QRect(x, 7, knob_width, knob_height), - clipping ? m_clip : m_knob, - QRect(0, 0, knob_width, knob_height)); + painter.drawPixmap(QRect(x, 7, knob_width, knob_height), + clipping ? m_clip : m_knob, + QRect(0, 0, knob_width, knob_height)); } } diff -r 57d192e26331 -r 13d9b422f7fe widgets/IconLoader.cpp --- a/widgets/IconLoader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/IconLoader.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -96,30 +96,35 @@ QString scalableName, nonScalableName; QPixmap pmap; + // attempt to load a pixmap with the right size and inversion nonScalableName = makeNonScalableFilename(name, size, invert); pmap = QPixmap(nonScalableName); - if (!pmap.isNull()) return pmap; - if (size > 0) { + if (pmap.isNull() && size > 0) { + // if that failed, load a scalable vector with the right + // inversion and scale it scalableName = makeScalableFilename(name, invert); pmap = loadScalable(scalableName, size); - if (!pmap.isNull()) return pmap; } - if (invert && shouldAutoInvert(name)) { - + if (pmap.isNull() && invert) { + // if that failed, and we were asking for an inverted pixmap, + // that may mean we don't have an inverted version of it. We + // could either auto-invert or use the uninverted version nonScalableName = makeNonScalableFilename(name, size, false); pmap = QPixmap(nonScalableName); - if (!pmap.isNull()) return invertPixmap(pmap); - if (size > 0) { + if (pmap.isNull() && size > 0) { scalableName = makeScalableFilename(name, false); pmap = loadScalable(scalableName, size); - if (!pmap.isNull()) return invertPixmap(pmap); + } + + if (!pmap.isNull() && shouldAutoInvert(name)) { + pmap = invertPixmap(pmap); } } - return QPixmap(); + return pmap; } QPixmap diff -r 57d192e26331 -r 13d9b422f7fe widgets/IconLoader.h --- a/widgets/IconLoader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/IconLoader.h Mon Sep 17 13:51:31 2018 +0100 @@ -38,4 +38,4 @@ #endif - + diff -r 57d192e26331 -r 13d9b422f7fe widgets/InteractiveFileFinder.cpp --- a/widgets/InteractiveFileFinder.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/InteractiveFileFinder.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -19,6 +19,7 @@ #include "data/fileio/DataFileReaderFactory.h" #include "rdf/RDFImporter.h" #include "rdf/RDFExporter.h" +#include "system/System.h" #include #include @@ -134,6 +135,11 @@ } break; + case SVGFile: + settingsKeyStub = "svg"; + filter = tr("Scalable Vector Graphics files (*.svg)\nAll files (*.*)"); + break; + case CSVFile: settingsKeyStub = "layer"; filter = tr("Comma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)"); @@ -151,9 +157,12 @@ }; if (lastPath == "") { - char *home = getenv("HOME"); - if (home) lastPath = home; - else lastPath = "."; + std::string home; + if (getEnvUtf8("HOME", home)) { + lastPath = QString::fromStdString(home); + } else { + lastPath = "."; + } } else if (QFileInfo(lastPath).isDir()) { lastPath = QFileInfo(lastPath).canonicalPath(); } else { @@ -282,6 +291,12 @@ filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); break; + case SVGFile: + settingsKeyStub = "savesvg"; + title = tr("Select a file to export to"); + filter = tr("Scalable Vector Graphics files (*.svg)\nAll files (*.*)"); + break; + case CSVFile: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); @@ -294,9 +309,12 @@ }; if (lastPath == "") { - char *home = getenv("HOME"); - if (home) lastPath = home; - else lastPath = "."; + std::string home; + if (getEnvUtf8("HOME", home)) { + lastPath = QString::fromStdString(home); + } else { + lastPath = "."; + } } else if (QFileInfo(lastPath).isDir()) { lastPath = QFileInfo(lastPath).canonicalPath(); } else { @@ -330,6 +348,8 @@ defaultSuffix = "wav"; } else if (type == ImageFile) { defaultSuffix = "png"; + } else if (type == SVGFile) { + defaultSuffix = "svg"; } else if (type == CSVFile) { defaultSuffix = "csv"; } @@ -450,6 +470,10 @@ settingsKeyStub = "image"; break; + case SVGFile: + settingsKeyStub = "svg"; + break; + case CSVFile: settingsKeyStub = "layer"; break; diff -r 57d192e26331 -r 13d9b422f7fe widgets/LEDButton.cpp --- a/widgets/LEDButton.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LEDButton.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -23,6 +23,7 @@ #include "LEDButton.h" +#include "WidgetScale.h" #include #include @@ -87,10 +88,10 @@ cerr << "LEDButton(" << this << ")::mousePressEvent" << endl; if (e->buttons() & Qt::LeftButton) { - toggle(); - bool newState = state(); - SVDEBUG << "emitting new state " << newState << endl; - emit stateChanged(newState); + toggle(); + bool newState = state(); + SVDEBUG << "emitting new state " << newState << endl; + emit stateChanged(newState); } } @@ -113,17 +114,17 @@ QColor color; QBrush brush; QPen pen; - + // First of all we want to know what area should be updated // Initialize coordinates, width, and height of the LED - int width = this->width(); + int width = this->width(); // Make sure the LED is round! if (width > this->height()) - width = this->height(); + width = this->height(); width -= 2; // leave one pixel border if (width < 0) - width = 0; + width = 0; paint.begin(this); @@ -153,25 +154,25 @@ int light_width = width; light_width *= 2; light_width /= 3; - + // Calculate the LED´s "light factor": int light_quote = (130*2/(light_width?light_width:1))+100; // Now draw the bright spot on the LED: while (light_width) { - color = color.light( light_quote ); // make color lighter - pen.setColor( color ); // set color as pen color - paint.setPen( pen ); // select the pen for drawing - paint.drawEllipse( pos, pos, light_width, light_width ); // draw the ellipse (circle) - light_width--; - if (!light_width) - break; - paint.drawEllipse( pos, pos, light_width, light_width ); - light_width--; - if (!light_width) - break; - paint.drawEllipse( pos, pos, light_width, light_width ); - pos++; light_width--; + color = color.light( light_quote ); // make color lighter + pen.setColor( color ); // set color as pen color + paint.setPen( pen ); // select the pen for drawing + paint.drawEllipse( pos, pos, light_width, light_width ); // draw the ellipse (circle) + light_width--; + if (!light_width) + break; + paint.drawEllipse( pos, pos, light_width, light_width ); + light_width--; + if (!light_width) + break; + paint.drawEllipse( pos, pos, light_width, light_width ); + pos++; light_width--; } paint.drawPoint(pos, pos); @@ -191,13 +192,13 @@ color = palette().light().color(); for (int arc = 120; arc < 2880; arc += 240) { - pen.setColor(color); - paint.setPen(pen); - int w = width - pen.width()/2; - paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle + arc, 240); - paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle - arc, 240); - color = color.dark(110); //FIXME: this should somehow use the contrast value - } // end for ( angle = 720; angle < 6480; angle += 160 ) + pen.setColor(color); + paint.setPen(pen); + int w = width - pen.width()/2; + paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle + arc, 240); + paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle - arc, 240); + color = color.dark(110); //FIXME: this should somehow use the contrast value + } // end for ( angle = 720; angle < 6480; angle += 160 ) paint.end(); } @@ -219,8 +220,8 @@ { if (led_state != state) { - led_state = state; - update(); + led_state = state; + update(); } } @@ -236,9 +237,9 @@ LEDButton::setColor(const QColor& col) { if(led_color!=col) { - led_color = col; - d->offcolor = col.dark(d->dark_factor); - update(); + led_color = col; + d->offcolor = col.dark(d->dark_factor); + update(); } } @@ -246,9 +247,9 @@ LEDButton::setDarkFactor(int darkfactor) { if (d->dark_factor != darkfactor) { - d->dark_factor = darkfactor; - d->offcolor = led_color.dark(darkfactor); - update(); + d->dark_factor = darkfactor; + d->offcolor = led_color.dark(darkfactor); + update(); } } @@ -279,12 +280,12 @@ QSize LEDButton::sizeHint() const { - return QSize(17, 17); + return WidgetScale::scaleQSize(QSize(17, 17)); } QSize LEDButton::minimumSizeHint() const { - return QSize(17, 17); + return WidgetScale::scaleQSize(QSize(17, 17)); } diff -r 57d192e26331 -r 13d9b422f7fe widgets/LEDButton.h --- a/widgets/LEDButton.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LEDButton.h Mon Sep 17 13:51:31 2018 +0100 @@ -25,8 +25,8 @@ sunken variant. This version also implements a simple button API. */ -#ifndef _LED_BUTTON_H_ -#define _LED_BUTTON_H_ +#ifndef SV_LED_BUTTON_H +#define SV_LED_BUTTON_H #include #include "base/Debug.h" diff -r 57d192e26331 -r 13d9b422f7fe widgets/LabelCounterInputDialog.cpp --- a/widgets/LabelCounterInputDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LabelCounterInputDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -22,7 +22,7 @@ #include LabelCounterInputDialog::LabelCounterInputDialog(Labeller *labeller, - QWidget *parent) : + QWidget *parent) : QDialog(parent), m_labeller(labeller) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/LayerTree.cpp --- a/widgets/LayerTree.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LayerTree.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -220,14 +220,14 @@ QVariant ModelMetadataModel::headerData(int section, - Qt::Orientation orientation, - int role) const + Qt::Orientation orientation, + int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - if (section == m_modelTypeColumn) return QVariant(tr("Type")); - else if (section == m_modelNameColumn) return QVariant(tr("Name")); - else if (section == m_modelMakerColumn) return QVariant(tr("Maker")); - else if (section == m_modelSourceColumn) return QVariant(tr("Source")); + if (section == m_modelTypeColumn) return QVariant(tr("Type")); + else if (section == m_modelNameColumn) return QVariant(tr("Name")); + else if (section == m_modelMakerColumn) return QVariant(tr("Maker")); + else if (section == m_modelSourceColumn) return QVariant(tr("Source")); } return QVariant(); @@ -238,7 +238,7 @@ { if (!parent.isValid()) { if (row >= (int)m_models.size()) return QModelIndex(); - return createIndex(row, column, (void *)0); + return createIndex(row, column, (void *)0); } return QModelIndex(); @@ -507,14 +507,14 @@ QVariant LayerTreeModel::headerData(int section, - Qt::Orientation orientation, - int role) const + Qt::Orientation orientation, + int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - if (section == m_layerNameColumn) return QVariant(tr("Layer")); + if (section == m_layerNameColumn) return QVariant(tr("Layer")); else if (section == m_layerVisibleColumn) return QVariant(tr("Shown")); else if (section == m_layerPlayedColumn) return QVariant(tr("Played")); - else if (section == m_modelNameColumn) return QVariant(tr("Model")); + else if (section == m_modelNameColumn) return QVariant(tr("Model")); } return QVariant(); @@ -531,7 +531,7 @@ if (!parent.isValid()) { if (row >= m_stack->getPaneCount() || column > 0) return QModelIndex(); - return createIndex(row, column, m_stack); + return createIndex(row, column, m_stack); } QObject *obj = static_cast(parent.internalPointer()); diff -r 57d192e26331 -r 13d9b422f7fe widgets/LevelPanToolButton.cpp --- a/widgets/LevelPanToolButton.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LevelPanToolButton.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -18,9 +18,12 @@ #include #include #include +#include #include #include +#include "base/Debug.h" + #include using std::cerr; using std::endl; @@ -49,6 +52,7 @@ setPopupMode(InstantPopup); setMenu(menu); + setToolTip(tr("Click to adjust level and pan")); setImageSize(m_pixels); setBigImageSize(m_pixelsBig); @@ -58,6 +62,25 @@ { } +void +LevelPanToolButton::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::MidButton || + ((e->button() == Qt::LeftButton) && + (e->modifiers() & Qt::ControlModifier))) { + m_lpw->setToDefault(); + e->accept(); + } else { + QToolButton::mousePressEvent(e); + } +} + +void +LevelPanToolButton::wheelEvent(QWheelEvent *e) +{ + m_lpw->wheelEvent(e); +} + float LevelPanToolButton::getLevel() const { @@ -110,6 +133,13 @@ } void +LevelPanToolButton::setMonitoringLevels(float left, float right) +{ + m_lpw->setMonitoringLevels(left, right); + update(); +} + +void LevelPanToolButton::setIncludeMute(bool include) { m_lpw->setIncludeMute(include); @@ -127,10 +157,10 @@ LevelPanToolButton::selfLevelChanged(float level) { if (level > 0.f) { - m_muted = false; + m_muted = false; } else { - m_muted = true; - m_savedLevel = 1.f; + m_muted = true; + m_savedLevel = 1.f; } update(); } @@ -138,17 +168,15 @@ void LevelPanToolButton::selfClicked() { - cerr << "selfClicked" << endl; - if (m_muted) { - m_muted = false; - m_lpw->setLevel(m_savedLevel); - emit levelChanged(m_savedLevel); + m_muted = false; + m_lpw->setLevel(m_savedLevel); + emit levelChanged(m_savedLevel); } else { - m_savedLevel = m_lpw->getLevel(); - m_muted = true; - m_lpw->setLevel(0.f); - emit levelChanged(0.f); + m_savedLevel = m_lpw->getLevel(); + m_muted = true; + m_lpw->setLevel(0.f); + emit levelChanged(0.f); } update(); } @@ -170,4 +198,18 @@ m_lpw->renderTo(this, QRectF(margin, margin, m_pixels, m_pixels), false); } +void +LevelPanToolButton::enterEvent(QEvent *e) +{ + QToolButton::enterEvent(e); + emit mouseEntered(); +} +void +LevelPanToolButton::leaveEvent(QEvent *e) +{ + QToolButton::enterEvent(e); + emit mouseLeft(); +} + + diff -r 57d192e26331 -r 13d9b422f7fe widgets/LevelPanToolButton.h --- a/widgets/LevelPanToolButton.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LevelPanToolButton.h Mon Sep 17 13:51:31 2018 +0100 @@ -37,9 +37,9 @@ bool includesMute() const; void setImageSize(int pixels); - + void setBigImageSize(int pixels); - + public slots: /// Set level in the range [0,1] -- will be rounded void setLevel(float); @@ -47,6 +47,9 @@ /// Set pan in the range [-1,1] -- will be rounded void setPan(float); + /// Set left and right peak monitoring levels in the range [0,1] + void setMonitoringLevels(float, float); + /// Specify whether the level range should include muting or not void setIncludeMute(bool); @@ -56,12 +59,19 @@ void levelChanged(float); void panChanged(float); + void mouseEntered(); + void mouseLeft(); + private slots: void selfLevelChanged(float); void selfClicked(); protected: - void paintEvent(QPaintEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void wheelEvent(QWheelEvent *e); LevelPanWidget *m_lpw; int m_pixels; diff -r 57d192e26331 -r 13d9b422f7fe widgets/LevelPanWidget.cpp --- a/widgets/LevelPanWidget.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LevelPanWidget.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -21,6 +21,8 @@ #include "layer/ColourMapper.h" #include "base/AudioLevel.h" +#include "WidgetScale.h" + #include #include #include @@ -28,108 +30,165 @@ using std::cerr; using std::endl; -static const int maxLevel = 4; // min is 0, may be mute or not depending on m_includeMute +/** + * Gain and pan scales: + * + * Gain: we have 5 circles vertically in the display, each of which + * has half-circle and full-circle versions, and we also have "no + * circles", so there are in total 11 distinct levels, which we refer + * to as "notches" and number 0-10. (We use "notch" because "level" is + * used by the external API to refer to audio gain.) + * + * i.e. the levels are represented by these (schematic, rotated to + * horizontal) displays: + * + * 0 X + * 1 [ + * 2 [] + * 3 [][ + * ... + * 9 [][][][][ + * 10 [][][][][] + * + * If we have mute enabled, then we map the range 0-10 to gain using + * AudioLevel::fader_to_* with the ShortFader type, which treats fader + * 0 as muted. If mute is disabled, then we map the range 1-10. + * + * We can also disable half-circles, which leaves the range unchanged + * but limits the notches to even values. + * + * Pan: we have 5 columns with no finer resolution, so we only have 2 + * possible pan values on each side of centre. + */ + static const int maxPan = 2; // range is -maxPan to maxPan LevelPanWidget::LevelPanWidget(QWidget *parent) : QWidget(parent), - m_level(maxLevel), + m_minNotch(0), + m_maxNotch(10), + m_notch(m_maxNotch), m_pan(0), + m_monitorLeft(-1), + m_monitorRight(-1), m_editable(true), - m_includeMute(true) + m_editing(false), + m_includeMute(true), + m_includeHalfSteps(true) { + setToolTip(tr("Drag vertically to adjust level, horizontally to adjust pan")); + setLevel(1.0); + setPan(0.0); } LevelPanWidget::~LevelPanWidget() { } +void +LevelPanWidget::setToDefault() +{ + setLevel(1.0); + setPan(0.0); + emitLevelChanged(); + emitPanChanged(); +} + QSize LevelPanWidget::sizeHint() const { - static double ratio = 0.0; - if (ratio == 0.0) { - double baseEm; -#ifdef Q_OS_MAC - baseEm = 17.0; -#else - baseEm = 15.0; -#endif - double em = QFontMetrics(QFont()).height(); - ratio = em / baseEm; - } - - int pixels = 40; - int scaled = int(pixels * ratio + 0.5); - if (pixels != 0 && scaled == 0) scaled = 1; - return QSize(scaled, scaled); + return WidgetScale::scaleQSize(QSize(40, 40)); } -static int -db_to_level(double db) +int +LevelPanWidget::clampNotch(int notch) const { - // Only if !m_includeMute, otherwise AudioLevel is used. - // Levels are: +6 0 -6 -12 -20 - assert(maxLevel == 4); - if (db > 3.) return 4; - else if (db > -3.) return 3; - else if (db > -9.) return 2; - else if (db > -16.) return 1; - else return 0; + if (notch < m_minNotch) notch = m_minNotch; + if (notch > m_maxNotch) notch = m_maxNotch; + if (!m_includeHalfSteps) { + notch = (notch / 2) * 2; + } + return notch; } -static double -level_to_db(int level) +int +LevelPanWidget::clampPan(int pan) const { - // Only if !m_includeMute, otherwise AudioLevel is used. - // Levels are: +6 0 -6 -12 -20 - assert(maxLevel == 4); - if (level >= 4) return 6.; - else if (level == 3) return 0.; - else if (level == 2) return -6.; - else if (level == 1) return -12.; - else return -20.; + if (pan < -maxPan) pan = -maxPan; + if (pan > maxPan) pan = maxPan; + return pan; +} + +int +LevelPanWidget::audioLevelToNotch(float audioLevel) const +{ + int notch = AudioLevel::multiplier_to_fader + (audioLevel, m_maxNotch, AudioLevel::ShortFader); + return clampNotch(notch); +} + +float +LevelPanWidget::notchToAudioLevel(int notch) const +{ + return float(AudioLevel::fader_to_multiplier + (notch, m_maxNotch, AudioLevel::ShortFader)); } void -LevelPanWidget::setLevel(float flevel) +LevelPanWidget::setLevel(float level) { - int level; - if (m_includeMute) { - level = AudioLevel::multiplier_to_fader - (flevel, maxLevel, AudioLevel::ShortFader); - } else { - level = db_to_level(AudioLevel::multiplier_to_dB(flevel)); - } - if (level < 0) level = 0; - if (level > maxLevel) level = maxLevel; - if (level != m_level) { - m_level = level; - float convertsTo = getLevel(); - if (fabsf(convertsTo - flevel) > 1e-5) { - emitLevelChanged(); - } - update(); + int notch = audioLevelToNotch(level); + if (notch != m_notch) { + m_notch = notch; + float convertsTo = getLevel(); + if (fabsf(convertsTo - level) > 1e-5) { + emitLevelChanged(); + } + update(); } } float LevelPanWidget::getLevel() const { - if (m_includeMute) { - return float(AudioLevel::fader_to_multiplier - (m_level, maxLevel, AudioLevel::ShortFader)); - } else { - return float(AudioLevel::dB_to_multiplier(level_to_db(m_level))); + return notchToAudioLevel(m_notch); +} + +int +LevelPanWidget::audioPanToPan(float audioPan) const +{ + int pan = int(round(audioPan * maxPan)); + pan = clampPan(pan); + return pan; +} + +float +LevelPanWidget::panToAudioPan(int pan) const +{ + return float(pan) / float(maxPan); +} + +void +LevelPanWidget::setPan(float fpan) +{ + int pan = audioPanToPan(fpan); + if (pan != m_pan) { + m_pan = pan; + update(); } } +float +LevelPanWidget::getPan() const +{ + return panToAudioPan(m_pan); +} + void -LevelPanWidget::setPan(float pan) +LevelPanWidget::setMonitoringLevels(float left, float right) { - m_pan = int(round(pan * maxPan)); - if (m_pan < -maxPan) m_pan = -maxPan; - if (m_pan > maxPan) m_pan = maxPan; + m_monitorLeft = left; + m_monitorRight = right; update(); } @@ -156,171 +215,190 @@ LevelPanWidget::setIncludeMute(bool include) { m_includeMute = include; + if (m_includeMute) { + m_minNotch = 0; + } else { + m_minNotch = 1; + } emitLevelChanged(); update(); } -float -LevelPanWidget::getPan() const -{ - return float(m_pan) / float(maxPan); -} - void LevelPanWidget::emitLevelChanged() { - cerr << "emitting levelChanged(" << getLevel() << ")" << endl; emit levelChanged(getLevel()); } void LevelPanWidget::emitPanChanged() { - cerr << "emitting panChanged(" << getPan() << ")" << endl; emit panChanged(getPan()); } void LevelPanWidget::mousePressEvent(QMouseEvent *e) { + if (e->button() == Qt::MidButton || + ((e->button() == Qt::LeftButton) && + (e->modifiers() & Qt::ControlModifier))) { + setToDefault(); + } else if (e->button() == Qt::LeftButton) { + m_editing = true; + mouseMoveEvent(e); + } +} + +void +LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) +{ mouseMoveEvent(e); + m_editing = false; } void LevelPanWidget::mouseMoveEvent(QMouseEvent *e) { if (!m_editable) return; + if (!m_editing) return; - int level, pan; - toCell(rect(), e->pos(), level, pan); - if (level == m_level && pan == m_pan) { - return; + int notch = coordsToNotch(rect(), e->pos()); + int pan = coordsToPan(rect(), e->pos()); + + if (notch == m_notch && pan == m_pan) { + return; } - if (level != m_level) { - m_level = level; - emitLevelChanged(); + if (notch != m_notch) { + m_notch = notch; + emitLevelChanged(); } if (pan != m_pan) { - m_pan = pan; - emitPanChanged(); + m_pan = pan; + emitPanChanged(); } update(); } void -LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) -{ - mouseMoveEvent(e); -} - -void LevelPanWidget::wheelEvent(QWheelEvent *e) { + int delta = m_wheelCounter.count(e); + + if (delta == 0) { + return; + } + if (e->modifiers() & Qt::ControlModifier) { - if (e->delta() > 0) { - if (m_pan < maxPan) { - ++m_pan; - emitPanChanged(); - update(); - } - } else { - if (m_pan > -maxPan) { - --m_pan; - emitPanChanged(); - update(); - } - } + m_pan = clampPan(m_pan + delta); + emitPanChanged(); + update(); } else { - if (e->delta() > 0) { - if (m_level < maxLevel) { - ++m_level; - emitLevelChanged(); - update(); - } - } else { - if (m_level > 0) { - --m_level; - emitLevelChanged(); - update(); - } - } + m_notch = clampNotch(m_notch + delta); + emitLevelChanged(); + update(); } } -void -LevelPanWidget::toCell(QRectF rect, QPointF loc, int &level, int &pan) const +int +LevelPanWidget::coordsToNotch(QRectF rect, QPointF loc) const { - double w = rect.width(), h = rect.height(); + double h = rect.height(); + + int nnotch = m_maxNotch + 1; + double cell = h / nnotch; + + int notch = int((h - (loc.y() - rect.y())) / cell); + notch = clampNotch(notch); + + return notch; +} + +int +LevelPanWidget::coordsToPan(QRectF rect, QPointF loc) const +{ + double w = rect.width(); int npan = maxPan * 2 + 1; - int nlevel = maxLevel + 1; + double cell = w / npan; - double wcell = w / npan, hcell = h / nlevel; + int pan = int((loc.x() - rect.x()) / cell) - maxPan; + pan = clampPan(pan); - level = int((h - (loc.y() - rect.y())) / hcell); - if (level < 0) level = 0; - if (level > maxLevel) level = maxLevel; - - pan = int((loc.x() - rect.x()) / wcell) - maxPan; - if (pan < -maxPan) pan = -maxPan; - if (pan > maxPan) pan = maxPan; + return pan; } QSizeF LevelPanWidget::cellSize(QRectF rect) const { double w = rect.width(), h = rect.height(); - int npan = maxPan * 2 + 1; - int nlevel = maxLevel + 1; - double wcell = w / npan, hcell = h / nlevel; + int ncol = maxPan * 2 + 1; + int nrow = m_maxNotch/2; + double wcell = w / ncol, hcell = h / nrow; return QSizeF(wcell, hcell); } QPointF -LevelPanWidget::cellCentre(QRectF rect, int level, int pan) const +LevelPanWidget::cellCentre(QRectF rect, int row, int col) const { QSizeF cs = cellSize(rect); - return QPointF(rect.x() + cs.width() * (pan + maxPan) + cs.width() / 2., - rect.y() + rect.height() - cs.height() * (level + 1) + cs.height() / 2.); + return QPointF(rect.x() + + cs.width() * (col + maxPan) + cs.width() / 2., + rect.y() + rect.height() - + cs.height() * (row + 1) + cs.height() / 2.); } QSizeF LevelPanWidget::cellLightSize(QRectF rect) const { - double extent = 3. / 4.; + double extent = 0.7; QSizeF cs = cellSize(rect); double m = std::min(cs.width(), cs.height()); return QSizeF(m * extent, m * extent); } QRectF -LevelPanWidget::cellLightRect(QRectF rect, int level, int pan) const +LevelPanWidget::cellLightRect(QRectF rect, int row, int col) const { QSizeF cls = cellLightSize(rect); - QPointF cc = cellCentre(rect, level, pan); + QPointF cc = cellCentre(rect, row, col); return QRectF(cc.x() - cls.width() / 2., - cc.y() - cls.height() / 2., - cls.width(), - cls.height()); + cc.y() - cls.height() / 2., + cls.width(), + cls.height()); } double LevelPanWidget::thinLineWidth(QRectF rect) const { double tw = ceil(rect.width() / (maxPan * 2. * 10.)); - double th = ceil(rect.height() / (maxLevel * 10.)); + double th = ceil(rect.height() / (m_maxNotch/2 * 10.)); return std::min(th, tw); } -static QColor -level_to_colour(int level) +double +LevelPanWidget::cornerRadius(QRectF rect) const { - assert(maxLevel == 4); - if (level == 0) return Qt::black; - else if (level == 1) return QColor(80, 0, 0); - else if (level == 2) return QColor(160, 0, 0); - else if (level == 3) return QColor(255, 0, 0); - else return QColor(255, 255, 0); + QSizeF cs = cellSize(rect); + double m = std::min(cs.width(), cs.height()); + return m / 5; +} + +QRectF +LevelPanWidget::cellOutlineRect(QRectF rect, int row, int col) const +{ + QRectF clr = cellLightRect(rect, row, col); + double adj = thinLineWidth(rect)/2 + 0.5; + return clr.adjusted(-adj, -adj, adj, adj); +} + +QColor +LevelPanWidget::cellToColour(int cell) const +{ + if (cell < 1) return Qt::black; + if (cell < 2) return QColor(80, 0, 0); + if (cell < 3) return QColor(160, 0, 0); + if (cell < 4) return QColor(255, 0, 0); + return QColor(255, 255, 0); } void @@ -330,51 +408,166 @@ paint.setRenderHint(QPainter::Antialiasing, true); + double thin = thinLineWidth(rect); + double radius = cornerRadius(rect); + + QColor columnBackground = QColor(180, 180, 180); + + bool monitoring = (m_monitorLeft > 0.f || m_monitorRight > 0.f); + QPen pen; - - double thin = thinLineWidth(rect); - - pen.setColor(QColor(127, 127, 127, 127)); - pen.setWidthF(cellLightSize(rect).width() + thin); - pen.setCapStyle(Qt::RoundCap); - paint.setPen(pen); + if (isEnabled()) { + pen.setColor(Qt::black); + } else { + pen.setColor(Qt::darkGray); + } + pen.setWidthF(thin); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::MiterJoin); for (int pan = -maxPan; pan <= maxPan; ++pan) { - paint.drawLine(cellCentre(rect, 0, pan), cellCentre(rect, maxLevel, pan)); + + paint.setPen(Qt::NoPen); + paint.setBrush(columnBackground); + + QRectF top = cellOutlineRect(rect, m_maxNotch/2 - 1, pan); + QRectF bottom = cellOutlineRect(rect, 0, pan); + paint.drawRoundedRect(QRectF(top.x(), + top.y(), + top.width(), + bottom.y() + bottom.height() - top.y()), + radius, radius); + + if (!asIfEditable && m_includeMute && m_notch == 0) { + // We will instead be drawing a single big X for mute, + // after this loop + continue; + } + + if (!monitoring && m_pan != pan) { + continue; + } + + int monitorNotch = 0; + if (monitoring) { + float rprop = float(pan - (-maxPan)) / float(maxPan * 2); + float lprop = float(maxPan - pan) / float(maxPan * 2); + float monitorLevel = + lprop * m_monitorLeft * m_monitorLeft + + rprop * m_monitorRight * m_monitorRight; + monitorNotch = audioLevelToNotch(monitorLevel); + } + + int firstCell = 0; + int lastCell = m_maxNotch / 2 - 1; + + for (int cell = firstCell; cell <= lastCell; ++cell) { + + QRectF clr = cellLightRect(rect, cell, pan); + + if (m_includeMute && m_pan == pan && m_notch == 0) { + // X for mute in the bottom cell + paint.setPen(pen); + paint.drawLine(clr.topLeft(), clr.bottomRight()); + paint.drawLine(clr.bottomLeft(), clr.topRight()); + break; + } + + const int none = 0, half = 1, full = 2; + + int fill = none; + + int outline = none; + if (m_pan == pan && m_notch > cell * 2 + 1) { + outline = full; + } else if (m_pan == pan && m_notch == cell * 2 + 1) { + outline = half; + } + + if (monitoring) { + if (monitorNotch > cell * 2 + 1) { + fill = full; + } else if (monitorNotch == cell * 2 + 1) { + fill = half; + } + } else { + if (isEnabled()) { + fill = outline; + } + } + + // If one of {fill, outline} is "full" and the other is + // "half", then we draw the "half" one first (because we + // need to erase half of it) + + if (fill == half || outline == half) { + if (fill == half) { + paint.setBrush(cellToColour(cell)); + } else { + paint.setBrush(Qt::NoBrush); + } + if (outline == half) { + paint.setPen(pen); + } else { + paint.setPen(Qt::NoPen); + } + + paint.drawRoundedRect(clr, radius, radius); + + paint.setBrush(columnBackground); + + if (cell == lastCell) { + QPen bgpen(pen); + bgpen.setColor(columnBackground); + paint.setPen(bgpen); + paint.drawRoundedRect(QRectF(clr.x(), + clr.y(), + clr.width(), + clr.height()/4), + radius, radius); + paint.drawRect(QRectF(clr.x(), + clr.y() + clr.height()/4, + clr.width(), + clr.height()/4)); + } else { + paint.setPen(Qt::NoPen); + QRectF cor = cellOutlineRect(rect, cell, pan); + paint.drawRect(QRectF(cor.x(), + cor.y() - 0.5, + cor.width(), + cor.height()/2)); + } + } + + if (outline == full || fill == full) { + + if (fill == full) { + paint.setBrush(cellToColour(cell)); + } else { + paint.setBrush(Qt::NoBrush); + } + if (outline == full) { + paint.setPen(pen); + } else { + paint.setPen(Qt::NoPen); + } + + paint.drawRoundedRect(clr, radius, radius); + } + } } - if (isEnabled()) { - pen.setColor(Qt::black); - } else { - pen.setColor(Qt::darkGray); - } - - if (!asIfEditable && m_includeMute && m_level == 0) { + if (!asIfEditable && m_includeMute && m_notch == 0) { + // The X for mute takes up the whole display when we're not + // being rendered in editable style + pen.setColor(Qt::black); pen.setWidthF(thin * 2); pen.setCapStyle(Qt::RoundCap); paint.setPen(pen); paint.drawLine(cellCentre(rect, 0, -maxPan), - cellCentre(rect, maxLevel, maxPan)); - paint.drawLine(cellCentre(rect, maxLevel, -maxPan), + cellCentre(rect, m_maxNotch/2 - 1, maxPan)); + paint.drawLine(cellCentre(rect, m_maxNotch/2 - 1, -maxPan), cellCentre(rect, 0, maxPan)); - return; - } - - pen.setWidthF(thin); - pen.setCapStyle(Qt::FlatCap); - paint.setPen(pen); - - for (int level = 0; level <= m_level; ++level) { - if (isEnabled()) { - paint.setBrush(level_to_colour(level)); - } - QRectF clr = cellLightRect(rect, level, m_pan); - if (m_includeMute && m_level == 0) { - paint.drawLine(clr.topLeft(), clr.bottomRight()); - paint.drawLine(clr.bottomLeft(), clr.topRight()); - } else { - paint.drawEllipse(clr); - } } } @@ -384,5 +577,17 @@ renderTo(this, rect(), m_editable); } +void +LevelPanWidget::enterEvent(QEvent *e) +{ + QWidget::enterEvent(e); + emit mouseEntered(); +} +void +LevelPanWidget::leaveEvent(QEvent *e) +{ + QWidget::enterEvent(e); + emit mouseLeft(); +} diff -r 57d192e26331 -r 13d9b422f7fe widgets/LevelPanWidget.h --- a/widgets/LevelPanWidget.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/LevelPanWidget.h Mon Sep 17 13:51:31 2018 +0100 @@ -17,6 +17,8 @@ #include +#include "WheelCounter.h" + /** * A simple widget for coarse level and pan control. */ @@ -29,7 +31,8 @@ LevelPanWidget(QWidget *parent = 0); ~LevelPanWidget(); - /// Return level as a gain value in the range [0,1] + /// Return level as a gain value. The basic level range is [0,1] but the + /// gain scale may go up to 4.0 float getLevel() const; /// Return pan as a value in the range [-1,1] @@ -47,43 +50,80 @@ QSize sizeHint() const; public slots: - /// Set level in the range [0,1] -- will be rounded + /// Set level. The basic level range is [0,1] but the scale may go + /// higher. The value will be rounded. void setLevel(float); - /// Set pan in the range [-1,1] -- will be rounded + /// Set pan in the range [-1,1]. The value will be rounded void setPan(float); + /// Set left and right peak monitoring levels in the range [0,1] + void setMonitoringLevels(float, float); + /// Specify whether the widget is editable or read-only (default editable) void setEditable(bool); /// Specify whether the level range should include muting or not void setIncludeMute(bool); + + /// Reset to default values + void setToDefault(); + + // public so it can be called from LevelPanToolButton (ew) + virtual void wheelEvent(QWheelEvent *ev); signals: - void levelChanged(float); - void panChanged(float); + void levelChanged(float); // range [0,1] + void panChanged(float); // range [-1,1] + void mouseEntered(); + void mouseLeft(); + protected: virtual void mousePressEvent(QMouseEvent *ev); virtual void mouseMoveEvent(QMouseEvent *ev); virtual void mouseReleaseEvent(QMouseEvent *ev); - virtual void wheelEvent(QWheelEvent *ev); virtual void paintEvent(QPaintEvent *ev); + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); void emitLevelChanged(); void emitPanChanged(); + + int m_minNotch; + int m_maxNotch; + int m_notch; + int m_pan; + float m_monitorLeft; + float m_monitorRight; + bool m_editable; + bool m_editing; + bool m_includeMute; + bool m_includeHalfSteps; + + WheelCounter m_wheelCounter; + + int clampNotch(int notch) const; + int clampPan(int pan) const; + + int audioLevelToNotch(float audioLevel) const; + float notchToAudioLevel(int notch) const; + + int audioPanToPan(float audioPan) const; + float panToAudioPan(int pan) const; + + int coordsToNotch(QRectF rect, QPointF pos) const; + int coordsToPan(QRectF rect, QPointF pos) const; + + QColor cellToColour(int cell) const; - int m_level; - int m_pan; - bool m_editable; - bool m_includeMute; - QSizeF cellSize(QRectF) const; - QPointF cellCentre(QRectF, int level, int pan) const; + QPointF cellCentre(QRectF, int row, int col) const; QSizeF cellLightSize(QRectF) const; - QRectF cellLightRect(QRectF, int level, int pan) const; + QRectF cellLightRect(QRectF, int row, int col) const; + QRectF cellOutlineRect(QRectF, int row, int col) const; double thinLineWidth(QRectF) const; - void toCell(QRectF, QPointF loc, int &level, int &pan) const; + double cornerRadius(QRectF) const; }; #endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/ListInputDialog.cpp --- a/widgets/ListInputDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ListInputDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -24,8 +24,8 @@ #include ListInputDialog::ListInputDialog(QWidget *parent, const QString &title, - const QString &labelText, const QStringList &list, - int current) : + const QString &labelText, const QStringList &list, + int current) : QDialog(parent), m_strings(list) { diff -r 57d192e26331 -r 13d9b422f7fe widgets/ModelDataTableDialog.cpp --- a/widgets/ModelDataTableDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ModelDataTableDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -61,7 +61,7 @@ toolbar = addToolBar(tr("Edit Toolbar")); - action = new QAction(il.load("datainsert"), tr("Insert New Item"), this); + action = new QAction(il.load("draw"), tr("Insert New Item"), this); action->setShortcut(tr("Insert")); action->setStatusTip(tr("Insert a new item")); connect(action, SIGNAL(triggered()), this, SLOT(insertRow())); @@ -208,6 +208,12 @@ void ModelDataTableDialog::makeCurrent(int row) { + if (m_table->rowCount() == 0 || + row >= m_table->rowCount() || + row < 0) { + return; + } + int rh = m_tableView->height() / m_tableView->rowHeight(0); int topRow = row - rh/4; if (topRow < 0) topRow = 0; @@ -255,12 +261,12 @@ void ModelDataTableDialog::currentChanged(const QModelIndex ¤t, - const QModelIndex &) + const QModelIndex &previous) { -// SVDEBUG << "ModelDataTableDialog::currentChanged: from " -// << previous.row() << ", " << previous.column() -// << " to " << current.row() << ", " << current.column() -// << endl; + SVDEBUG << "ModelDataTableDialog::currentChanged: from " + << previous.row() << ", " << previous.column() + << " to " << current.row() << ", " << current.column() + << endl; m_currentRow = current.row(); m_table->setCurrentRow(m_currentRow); } @@ -274,10 +280,17 @@ void ModelDataTableDialog::deleteRows() { - // not efficient - while (m_tableView->selectionModel()->hasSelection()) { - m_table->removeRow - (m_tableView->selectionModel()->selection().indexes().begin()->row()); + std::set selectedRows; + if (m_tableView->selectionModel()->hasSelection()) { + for (const auto &ix: m_tableView->selectionModel()->selectedIndexes()) { + selectedRows.insert(ix.row()); + } + } + // Remove rows in reverse order, so as not to pull the rug from + // under our own feet + for (auto ri = selectedRows.rbegin(); ri != selectedRows.rend(); ++ri) { + SVDEBUG << "ModelDataTableDialog: removing row " << *ri << endl; + m_table->removeRow(*ri); } } diff -r 57d192e26331 -r 13d9b422f7fe widgets/ModelDataTableDialog.h --- a/widgets/ModelDataTableDialog.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ModelDataTableDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MODEL_DATA_TABLE_DIALOG_H_ -#define _MODEL_DATA_TABLE_DIALOG_H_ +#ifndef SV_MODEL_DATA_TABLE_DIALOG_H +#define SV_MODEL_DATA_TABLE_DIALOG_H #include diff -r 57d192e26331 -r 13d9b422f7fe widgets/NotifyingComboBox.h --- a/widgets/NotifyingComboBox.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/NotifyingComboBox.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _NOTIFYING_COMBO_BOX_H_ -#define _NOTIFYING_COMBO_BOX_H_ +#ifndef SV_NOTIFYING_COMBO_BOX_H +#define SV_NOTIFYING_COMBO_BOX_H #include @@ -26,8 +26,8 @@ class NotifyingComboBox : public QComboBox { Q_OBJECT + public: - NotifyingComboBox(QWidget *parent = 0) : QComboBox(parent) { } diff -r 57d192e26331 -r 13d9b422f7fe widgets/NotifyingPushButton.h --- a/widgets/NotifyingPushButton.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/NotifyingPushButton.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,21 +13,22 @@ COPYING included with this distribution for more information. */ -#ifndef _NOTIFYING_PUSH_BUTTON_H_ -#define _NOTIFYING_PUSH_BUTTON_H_ +#ifndef SV_NOTIFYING_PUSH_BUTTON_H +#define SV_NOTIFYING_PUSH_BUTTON_H #include /** - * Very trivial enhancement to QPushButton to make it emit signals when - * the mouse enters and leaves (for context help). + * Very trivial enhancement to QPushButton to make it emit signals + * when the mouse enters and leaves (for context help). See also + * NotifyingToolButton */ class NotifyingPushButton : public QPushButton { Q_OBJECT + public: - NotifyingPushButton(QWidget *parent = 0) : QPushButton(parent) { } diff -r 57d192e26331 -r 13d9b422f7fe widgets/NotifyingToolButton.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/NotifyingToolButton.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,35 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "NotifyingToolButton.h" + +NotifyingToolButton::~NotifyingToolButton() +{ +} + +void +NotifyingToolButton::enterEvent(QEvent *e) +{ + QToolButton::enterEvent(e); + emit mouseEntered(); +} + +void +NotifyingToolButton::leaveEvent(QEvent *e) +{ + QToolButton::enterEvent(e); + emit mouseLeft(); +} + diff -r 57d192e26331 -r 13d9b422f7fe widgets/NotifyingToolButton.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/NotifyingToolButton.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_NOTIFYING_TOOL_BUTTON_H +#define SV_NOTIFYING_TOOL_BUTTON_H + +#include + +/** + * Very trivial enhancement to QToolButton to make it emit signals + * when the mouse enters and leaves (for context help). See also + * NotifyingPushButton + */ + +class NotifyingToolButton : public QToolButton +{ + Q_OBJECT + +public: + NotifyingToolButton(QWidget *parent = 0) : + QToolButton(parent) { } + + virtual ~NotifyingToolButton(); + +signals: + void mouseEntered(); + void mouseLeft(); + +protected: + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); +}; + +#endif + diff -r 57d192e26331 -r 13d9b422f7fe widgets/Panner.cpp --- a/widgets/Panner.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/Panner.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -20,6 +20,8 @@ #include #include +#include "WidgetScale.h" + #include #include @@ -62,6 +64,12 @@ void Panner::scroll(bool up) { + scroll(up, 1); +} + +void +Panner::scroll(bool up, int count) +{ float unit = m_scrollUnit; if (unit == 0.f) { unit = float(m_rectHeight) / (6 * float(height())); @@ -69,9 +77,9 @@ } if (!up) { - m_rectY += unit; + m_rectY += unit * float(count); } else { - m_rectY -= unit; + m_rectY -= unit * float(count); } normalise(); @@ -132,7 +140,8 @@ void Panner::wheelEvent(QWheelEvent *e) { - scroll(e->delta() > 0); + int delta = m_wheelCounter.count(e); + scroll(delta > 0, abs(delta)); } void @@ -156,9 +165,13 @@ QColor bg(palette().background().color()); bg.setAlpha(m_backgroundAlpha); - paint.setPen(palette().dark().color()); + int penWidth = WidgetScale::scalePixelSize(1); + if (penWidth < 1) penWidth = 1; + paint.setPen(QPen(palette().dark().color(), penWidth)); + paint.setBrush(bg); - paint.drawRect(0, 0, width()-1, height()-1); + paint.drawRect(penWidth/2, penWidth/2, + width()-penWidth/2-1, height()-penWidth/2-1); QColor hl(m_thumbColour); hl.setAlpha(m_thumbAlpha); diff -r 57d192e26331 -r 13d9b422f7fe widgets/Panner.h --- a/widgets/Panner.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/Panner.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,11 +13,13 @@ COPYING included with this distribution for more information. */ -#ifndef _PANNER_H_ -#define _PANNER_H_ +#ifndef SV_PANNER_H +#define SV_PANNER_H #include +#include "WheelCounter.h" + class Panner : public QWidget { Q_OBJECT @@ -109,6 +111,12 @@ */ void scroll(bool up); + /** + * Move up (if up is true) or down a bit. This is basically the + * same action as rolling the mouse wheel n notches. + */ + void scroll(bool up, int n); + void resetToDefault(); protected: @@ -145,6 +153,8 @@ QPoint m_clickPos; float m_dragStartX; float m_dragStartY; + + WheelCounter m_wheelCounter; }; #endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/PluginParameterDialog.cpp --- a/widgets/PluginParameterDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/PluginParameterDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -20,6 +20,7 @@ #include "TextAbbrev.h" #include "IconLoader.h" +#include "WidgetScale.h" #include #include @@ -43,7 +44,7 @@ #include "base/Debug.h" PluginParameterDialog::PluginParameterDialog(Vamp::PluginBase *plugin, - QWidget *parent) : + QWidget *parent) : QDialog(parent), m_plugin(plugin), m_channel(-1), @@ -88,10 +89,6 @@ QLabel *copyrightLabel = new QLabel(plugin->getCopyright().c_str()); copyrightLabel->setWordWrap(true); -// QLabel *typeLabel = new QLabel(plugin->getType().c_str()); -// typeLabel->setWordWrap(true); -// typeLabel->setFont(boldFont); - QLabel *descriptionLabel = 0; if (plugin->getDescription() != "") { descriptionLabel = new QLabel(plugin->getDescription().c_str()); @@ -108,7 +105,7 @@ m_moreInfo = new QPushButton; m_moreInfo->setIcon(IconLoader().load("info")); - m_moreInfo->setFixedSize(QSize(16, 16)); + m_moreInfo->setFixedSize(WidgetScale::scaleQSize(QSize(16, 16))); connect(m_moreInfo, SIGNAL(clicked()), this, SLOT(moreInfo())); subgrid->addWidget(m_moreInfo, row, 2, Qt::AlignTop | Qt::AlignRight); m_moreInfo->hide(); @@ -116,9 +113,6 @@ row++; if (descriptionLabel) { -// label = new QLabel(tr("Description:")); -// label->setAlignment(Qt::AlignTop | Qt::AlignLeft); -// subgrid->addWidget(label, row, 0); subgrid->addWidget(descriptionLabel, row, 1, 1, 2); row++; } @@ -131,12 +125,6 @@ row++; } -// label = new QLabel(tr("Type:")); -// label->setAlignment(Qt::AlignTop | Qt::AlignLeft); -// subgrid->addWidget(label, row, 0); -// subgrid->addWidget(typeLabel, row, 1); -// row++; - label = new QLabel(tr("Maker:")); label->setAlignment(Qt::AlignTop | Qt::AlignLeft); subgrid->addWidget(label, row, 0); @@ -595,8 +583,6 @@ void PluginParameterDialog::setAdvancedVisible(bool visible) { -// m_advanced->setVisible(visible); - if (visible) { m_advancedButton->setText(tr("Advanced <<")); m_advancedButton->setChecked(true); @@ -607,22 +593,14 @@ m_advancedButton->setChecked(false); } -// cerr << "resize to " << sizeHint().width() << " x " << sizeHint().height() << endl; - -// setMinimumHeight(sizeHint().height()); adjustSize(); -// (sizeHint()); - m_advancedVisible = visible; QSettings settings; settings.beginGroup("PluginParameterDialog"); settings.setValue("advancedvisible", visible); settings.endGroup(); - -// if (visible) setMaximumHeight(sizeHint().height()); -// adjustSize(); } void diff -r 57d192e26331 -r 13d9b422f7fe widgets/PluginPathConfigurator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/PluginPathConfigurator.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,384 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "PluginPathConfigurator.h" +#include "PluginReviewDialog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "IconLoader.h" +#include "WidgetScale.h" + +#include "plugin/PluginPathSetter.h" + +PluginPathConfigurator::PluginPathConfigurator(QWidget *parent) : + QFrame(parent) +{ + m_layout = new QGridLayout; + setLayout(m_layout); + + QHBoxLayout *buttons = new QHBoxLayout; + + m_down = new QPushButton; + m_down->setIcon(IconLoader().load("down")); + m_down->setToolTip(tr("Move the selected location later in the list")); + connect(m_down, SIGNAL(clicked()), this, SLOT(downClicked())); + buttons->addWidget(m_down); + + m_up = new QPushButton; + m_up->setIcon(IconLoader().load("up")); + m_up->setToolTip(tr("Move the selected location earlier in the list")); + connect(m_up, SIGNAL(clicked()), this, SLOT(upClicked())); + buttons->addWidget(m_up); + + m_add = new QPushButton; + m_add->setIcon(IconLoader().load("plus")); + m_add->setToolTip(tr("Add a new location to the list")); + connect(m_add, SIGNAL(clicked()), this, SLOT(addClicked())); + buttons->addWidget(m_add); + + m_delete = new QPushButton; + m_delete->setIcon(IconLoader().load("datadelete")); + m_delete->setToolTip(tr("Remove the selected location from the list")); + connect(m_delete, SIGNAL(clicked()), this, SLOT(deleteClicked())); + buttons->addWidget(m_delete); + + m_reset = new QPushButton; + m_reset->setText(tr("Reset to Default")); + m_reset->setToolTip(tr("Reset the list for this plugin type to its default")); + connect(m_reset, SIGNAL(clicked()), this, SLOT(resetClicked())); + buttons->addWidget(m_reset); + + buttons->addStretch(50); + + m_seePlugins = new QPushButton; + m_seePlugins->setText(tr("Review plugins...")); + connect(m_seePlugins, SIGNAL(clicked()), this, SLOT(seePluginsClicked())); + buttons->addWidget(m_seePlugins); + + int row = 0; + + m_header = new QLabel; + m_header->setText(tr("Plugin locations for plugin type:")); + m_layout->addWidget(m_header, row, 0); + + m_pluginTypeSelector = new QComboBox; + m_layout->addWidget(m_pluginTypeSelector, row, 1, Qt::AlignLeft); + connect(m_pluginTypeSelector, SIGNAL(currentTextChanged(QString)), + this, SLOT(currentTypeChanged(QString))); + + m_layout->setColumnStretch(1, 10); + ++row; + + m_list = new QListWidget; + m_layout->addWidget(m_list, row, 0, 1, 3); + m_layout->setRowStretch(row, 20); + connect(m_list, SIGNAL(currentRowChanged(int)), + this, SLOT(currentLocationChanged(int))); + ++row; + + m_layout->addLayout(buttons, row, 0, 1, 3); + + ++row; + + m_envOverride = new QCheckBox; + connect(m_envOverride, SIGNAL(stateChanged(int)), + this, SLOT(envOverrideChanged(int))); + m_layout->addWidget(m_envOverride, row, 0, 1, 3); + ++row; +} + +PluginPathConfigurator::~PluginPathConfigurator() +{ +} + +QString +PluginPathConfigurator::getLabelFor(PluginPathSetter::TypeKey key) +{ + if (key.second == KnownPlugins::FormatNative) { + switch (key.first) { + case KnownPlugins::VampPlugin: + return tr("Vamp"); + case KnownPlugins::LADSPAPlugin: + return tr("LADSPA"); + case KnownPlugins::DSSIPlugin: + return tr("DSSI"); + } + } else if (key.second == KnownPlugins::FormatNonNative32Bit) { + switch (key.first) { + case KnownPlugins::VampPlugin: + return tr("Vamp (32-bit)"); + case KnownPlugins::LADSPAPlugin: + return tr("LADSPA (32-bit)"); + case KnownPlugins::DSSIPlugin: + return tr("DSSI (32-bit)"); + } + } + SVCERR << "PluginPathConfigurator::getLabelFor: WARNING: " + << "Unknown format value " << key.second << endl; + return ""; +} + +PluginPathSetter::TypeKey +PluginPathConfigurator::getKeyForLabel(QString label) +{ + for (const auto &p: m_paths) { + auto key = p.first; + if (getLabelFor(key) == label) { + return key; + } + } + SVCERR << "PluginPathConfigurator::getKeyForLabel: WARNING: " + << "Unrecognised label \"" << label << "\"" << endl; + return { KnownPlugins::VampPlugin, KnownPlugins::FormatNative }; +} + +void +PluginPathConfigurator::setPaths(PluginPathSetter::Paths paths) +{ + m_paths = paths; + + m_defaultPaths = PluginPathSetter::getDefaultPaths(); + + m_pluginTypeSelector->clear(); + for (const auto &p: paths) { + m_pluginTypeSelector->addItem(getLabelFor(p.first)); + } + + populate(); +} + +void +PluginPathConfigurator::populate() +{ + m_list->clear(); + + if (m_paths.empty()) return; + + populateFor(m_paths.begin()->first, -1); +} + +void +PluginPathConfigurator::populateFor(PluginPathSetter::TypeKey key, + int makeCurrent) +{ + QString envVariable = m_paths.at(key).envVariable; + bool useEnvVariable = m_paths.at(key).useEnvVariable; + QString envVarValue = + PluginPathSetter::getOriginalEnvironmentValue(envVariable); + QString currentValueRubric; + if (envVarValue == QString()) { + currentValueRubric = tr("(Variable is currently unset)"); + } else { + if (envVarValue.length() > 100) { + QString envVarStart = envVarValue.left(95); + currentValueRubric = tr("(Current value begins: \"%1 ...\")") + .arg(envVarStart); + } else { + currentValueRubric = tr("(Currently set to: \"%1\")") + .arg(envVarValue); + } + } + m_envOverride->setText + (tr("Allow the %1 environment variable to take priority over this\n%2") + .arg(envVariable) + .arg(currentValueRubric)); + m_envOverride->setCheckState(useEnvVariable ? Qt::Checked : Qt::Unchecked); + + m_list->clear(); + + for (int i = 0; i < m_pluginTypeSelector->count(); ++i) { + if (getLabelFor(key) == m_pluginTypeSelector->itemText(i)) { + m_pluginTypeSelector->blockSignals(true); + m_pluginTypeSelector->setCurrentIndex(i); + m_pluginTypeSelector->blockSignals(false); + } + } + + QStringList path = m_paths.at(key).directories; + + for (int i = 0; i < path.size(); ++i) { + m_list->addItem(path[i]); + } + + if (makeCurrent < path.size()) { + m_list->setCurrentRow(makeCurrent); + currentLocationChanged(makeCurrent); + } +} + +void +PluginPathConfigurator::currentLocationChanged(int i) +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + QStringList path = m_paths.at(key).directories; + m_up->setEnabled(i > 0); + m_down->setEnabled(i >= 0 && i + 1 < path.size()); + m_delete->setEnabled(i >= 0 && i < path.size()); + m_reset->setEnabled(path != m_defaultPaths.at(key).directories); +} + +void +PluginPathConfigurator::currentTypeChanged(QString label) +{ + populateFor(getKeyForLabel(label), -1); +} + +void +PluginPathConfigurator::envOverrideChanged(int state) +{ + bool useEnvVariable = (state == Qt::Checked); + + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + + auto newEntry = m_paths.at(key); + newEntry.useEnvVariable = useEnvVariable; + m_paths[key] = newEntry; + + emit pathsChanged(); +} + +void +PluginPathConfigurator::upClicked() +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + QStringList path = m_paths.at(key).directories; + + int current = m_list->currentRow(); + if (current <= 0) return; + + QStringList newPath; + for (int i = 0; i < path.size(); ++i) { + if (i + 1 == current) { + newPath.push_back(path[i+1]); + newPath.push_back(path[i]); + ++i; + } else { + newPath.push_back(path[i]); + } + } + + auto newEntry = m_paths.at(key); + newEntry.directories = newPath; + m_paths[key] = newEntry; + + populateFor(key, current - 1); + + emit pathsChanged(); +} + +void +PluginPathConfigurator::downClicked() +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + QStringList path = m_paths.at(key).directories; + + int current = m_list->currentRow(); + if (current < 0 || current + 1 >= path.size()) return; + + QStringList newPath; + for (int i = 0; i < path.size(); ++i) { + if (i == current) { + newPath.push_back(path[i+1]); + newPath.push_back(path[i]); + ++i; + } else { + newPath.push_back(path[i]); + } + } + + auto newEntry = m_paths.at(key); + newEntry.directories = newPath; + m_paths[key] = newEntry; + + populateFor(key, current + 1); + + emit pathsChanged(); +} + +void +PluginPathConfigurator::addClicked() +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + + QString newDir = QFileDialog::getExistingDirectory + (this, tr("Choose directory to add")); + + if (newDir == QString()) return; + + auto newEntry = m_paths.at(key); + newEntry.directories.push_back(newDir); + m_paths[key] = newEntry; + + populateFor(key, newEntry.directories.size() - 1); + + emit pathsChanged(); +} + +void +PluginPathConfigurator::deleteClicked() +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + QStringList path = m_paths.at(key).directories; + + int current = m_list->currentRow(); + if (current < 0) return; + + QStringList newPath; + for (int i = 0; i < path.size(); ++i) { + if (i != current) { + newPath.push_back(path[i]); + } + } + + auto newEntry = m_paths.at(key); + newEntry.directories = newPath; + m_paths[key] = newEntry; + + populateFor(key, current < newPath.size() ? current : current-1); + + emit pathsChanged(); +} + +void +PluginPathConfigurator::resetClicked() +{ + QString label = m_pluginTypeSelector->currentText(); + PluginPathSetter::TypeKey key = getKeyForLabel(label); + m_paths[key] = m_defaultPaths[key]; + populateFor(key, -1); + + emit pathsChanged(); +} + +void +PluginPathConfigurator::seePluginsClicked() +{ + PluginReviewDialog dialog(this); + dialog.populate(); + dialog.exec(); +} + diff -r 57d192e26331 -r 13d9b422f7fe widgets/PluginPathConfigurator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/PluginPathConfigurator.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,81 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_PLUGIN_PATH_CONFIGURATOR_H +#define SV_PLUGIN_PATH_CONFIGURATOR_H + +#include +#include + +class QLabel; +class QWidget; +class QListWidget; +class QPushButton; +class QGridLayout; +class QComboBox; +class QCheckBox; + +#include "plugin/PluginPathSetter.h" + +class PluginPathConfigurator : public QFrame +{ + Q_OBJECT + +public: + PluginPathConfigurator(QWidget *parent = 0); + ~PluginPathConfigurator(); + + void setPaths(PluginPathSetter::Paths paths); + PluginPathSetter::Paths getPaths() const { return m_paths; } + +signals: + void pathsChanged(); + +private slots: + void upClicked(); + void downClicked(); + void addClicked(); + void deleteClicked(); + void resetClicked(); + void currentTypeChanged(QString); + void currentLocationChanged(int); + void envOverrideChanged(int); + void seePluginsClicked(); + +private: + QGridLayout *m_layout; + QLabel *m_header; + QComboBox *m_pluginTypeSelector; + QListWidget *m_list; + QPushButton *m_seePlugins; + QPushButton *m_up; + QPushButton *m_down; + QPushButton *m_add; + QPushButton *m_delete; + QPushButton *m_reset; + QCheckBox *m_envOverride; + + PluginPathSetter::Paths m_paths; + PluginPathSetter::Paths m_defaultPaths; + + void populate(); + void populateFor(PluginPathSetter::TypeKey, int makeCurrent); + + QString getLabelFor(PluginPathSetter::TypeKey); + PluginPathSetter::TypeKey getKeyForLabel(QString label); +}; + +#endif + + diff -r 57d192e26331 -r 13d9b422f7fe widgets/PluginReviewDialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/PluginReviewDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,140 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "PluginReviewDialog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "plugin/FeatureExtractionPluginFactory.h" +#include "plugin/RealTimePluginFactory.h" + +PluginReviewDialog::PluginReviewDialog(QWidget *parent) : + QDialog(parent) +{ + setWindowTitle(tr("Plugins Loaded")); + + QGridLayout *layout = new QGridLayout; + setLayout(layout); + + m_table = new QTableWidget; + layout->addWidget(m_table, 0, 1); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + layout->addWidget(bb, 1, 1); + connect(bb, SIGNAL(rejected()), this, SLOT(close())); +} + +PluginReviewDialog::~PluginReviewDialog() +{ +} + +void +PluginReviewDialog::populate() +{ + FeatureExtractionPluginFactory *feFactory = + FeatureExtractionPluginFactory::instance(); + QString err; + std::vector feIds = feFactory->getPluginIdentifiers(err); + + RealTimePluginFactory *dssiFactory = + RealTimePluginFactory::instance("dssi"); + std::vector dssiIds = dssiFactory->getPluginIdentifiers(); + + RealTimePluginFactory *ladspaFactory = + RealTimePluginFactory::instance("ladspa"); + std::vector ladspaIds = ladspaFactory->getPluginIdentifiers(); + + m_table->setRowCount(int(feIds.size() + dssiIds.size() + ladspaIds.size())); + m_table->setColumnCount(5); + + QStringList headers; + int typeCol = 0, libCol = 1, idCol = 2, dirCol = 3, nameCol = 4; + headers << tr("Type") << tr("Library") + << tr("Identifier") << tr("Found in") << tr("Name"); + m_table->setHorizontalHeaderLabels(headers); + + int row = 0; + + for (QString id: feIds) { + auto staticData = feFactory->getPluginStaticData(id); + m_table->setItem(row, typeCol, new QTableWidgetItem + (tr("Vamp"))); + m_table->setItem(row, idCol, new QTableWidgetItem + (QString::fromStdString(staticData.basic.identifier))); + m_table->setItem(row, nameCol, new QTableWidgetItem + (QString::fromStdString(staticData.basic.name))); + QString path = feFactory->getPluginLibraryPath(id); + m_table->setItem(row, libCol, new QTableWidgetItem + (QFileInfo(path).fileName())); + m_table->setItem(row, dirCol, new QTableWidgetItem + (QFileInfo(path).path())); + row++; + } + + for (QString id: dssiIds) { + auto descriptor = dssiFactory->getPluginDescriptor(id); + if (!descriptor) continue; + m_table->setItem(row, typeCol, new QTableWidgetItem + (tr("DSSI"))); + m_table->setItem(row, idCol, new QTableWidgetItem + (QString::fromStdString(descriptor->label))); + m_table->setItem(row, nameCol, new QTableWidgetItem + (QString::fromStdString(descriptor->name))); + QString path = dssiFactory->getPluginLibraryPath(id); + m_table->setItem(row, libCol, new QTableWidgetItem + (QFileInfo(path).fileName())); + m_table->setItem(row, dirCol, new QTableWidgetItem + (QFileInfo(path).path())); + row++; + } + + for (QString id: ladspaIds) { + auto descriptor = ladspaFactory->getPluginDescriptor(id); + if (!descriptor) continue; + m_table->setItem(row, typeCol, new QTableWidgetItem + (tr("LADSPA"))); + m_table->setItem(row, idCol, new QTableWidgetItem + (QString::fromStdString(descriptor->label))); + m_table->setItem(row, nameCol, new QTableWidgetItem + (QString::fromStdString(descriptor->name))); + QString path = ladspaFactory->getPluginLibraryPath(id); + m_table->setItem(row, libCol, new QTableWidgetItem + (QFileInfo(path).fileName())); + m_table->setItem(row, dirCol, new QTableWidgetItem + (QFileInfo(path).path())); + row++; + } + + m_table->setSortingEnabled(true); + m_table->setSelectionMode(QAbstractItemView::NoSelection); + m_table->resizeColumnsToContents(); + + int twidth = m_table->horizontalHeader()->length(); + int theight = m_table->verticalHeader()->length(); + + QDesktopWidget *desktop = QApplication::desktop(); + QRect available = desktop->availableGeometry(); + + int width = std::min(twidth + 30, (available.width() * 3) / 4); + int height = std::min(theight + 30, (available.height() * 3) / 4); + + resize(width, height); +} + diff -r 57d192e26331 -r 13d9b422f7fe widgets/PluginReviewDialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/PluginReviewDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,37 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_PLUGIN_REVIEW_DIALOG_H +#define SV_PLUGIN_REVIEW_DIALOG_H + +#include +#include + +class QEvent; + +class PluginReviewDialog : public QDialog +{ + Q_OBJECT + +public: + PluginReviewDialog(QWidget *parent = 0); + ~PluginReviewDialog(); + + void populate(); + +private: + QTableWidget *m_table; +}; + +#endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/ProgressDialog.cpp --- a/widgets/ProgressDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ProgressDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -19,14 +19,19 @@ #include #include -ProgressDialog::ProgressDialog(QString message, bool cancellable, - int timeBeforeShow, QWidget *parent) : +ProgressDialog::ProgressDialog(QString message, + bool cancellable, + int timeBeforeShow, + QWidget *parent, + Qt::WindowModality modality) : m_showTimer(0), m_timerElapsed(false), m_cancelled(false) { m_dialog = new QProgressDialog(message, cancellable ? tr("Cancel") : 0, 0, 100, parent); + m_dialog->setWindowModality(modality); + if (timeBeforeShow > 0) { m_dialog->hide(); m_showTimer = new QTimer; @@ -97,9 +102,6 @@ ProgressDialog::setProgress(int percentage) { if (percentage > m_dialog->value()) { - - m_dialog->setValue(percentage); - if (percentage >= 100 && isDefinite()) { m_dialog->hide(); } else if (m_timerElapsed && !m_dialog->isVisible()) { @@ -107,8 +109,7 @@ m_dialog->show(); m_dialog->raise(); } - - qApp->processEvents(); + m_dialog->setValue(percentage); // processes event loop when modal + if (!m_dialog->isModal()) qApp->processEvents(); } -} - +} \ No newline at end of file diff -r 57d192e26331 -r 13d9b422f7fe widgets/ProgressDialog.h --- a/widgets/ProgressDialog.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/ProgressDialog.h Mon Sep 17 13:51:31 2018 +0100 @@ -25,8 +25,11 @@ Q_OBJECT public: - ProgressDialog(QString message, bool cancellable, - int timeBeforeShow = 0, QWidget *parent = 0); + ProgressDialog(QString message, + bool cancellable, + int timeBeforeShow = 0, + QWidget *parent = 0, + Qt::WindowModality modality = Qt::NonModal); virtual ~ProgressDialog(); virtual bool isDefinite() const; diff -r 57d192e26331 -r 13d9b422f7fe widgets/PropertyBox.cpp --- a/widgets/PropertyBox.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/PropertyBox.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -20,23 +20,28 @@ #include "base/PlayParameters.h" #include "base/PlayParameterRepository.h" #include "layer/Layer.h" -#include "layer/ColourDatabase.h" #include "base/UnitDatabase.h" #include "base/RangeMapper.h" #include "AudioDial.h" #include "LEDButton.h" #include "IconLoader.h" +#include "LevelPanWidget.h" +#include "LevelPanToolButton.h" +#include "WidgetScale.h" #include "NotifyingCheckBox.h" #include "NotifyingComboBox.h" #include "NotifyingPushButton.h" -#include "ColourNameDialog.h" +#include "NotifyingToolButton.h" +#include "ColourComboBox.h" +#include "ColourMapComboBox.h" #include #include #include #include +#include #include #include #include @@ -57,7 +62,7 @@ { #ifdef DEBUG_PROPERTY_BOX cerr << "PropertyBox[" << this << "(\"" << - container->getPropertyContainerName() << "\" at " << container << ")]::PropertyBox" << endl; + container->getPropertyContainerName() << "\" at " << container << ")]::PropertyBox" << endl; #endif m_mainBox = new QVBoxLayout; @@ -93,7 +98,7 @@ size_t i; for (i = 0; i < properties.size(); ++i) { - updatePropertyEditor(properties[i]); + updatePropertyEditor(properties[i]); } blockSignals(false); @@ -103,9 +108,6 @@ connect(UnitDatabase::getInstance(), SIGNAL(unitDatabaseChanged()), this, SLOT(unitDatabaseChanged())); - connect(ColourDatabase::getInstance(), SIGNAL(colourDatabaseChanged()), - this, SLOT(colourDatabaseChanged())); - #ifdef DEBUG_PROPERTY_BOX cerr << "PropertyBox[" << this << "]::PropertyBox returning" << endl; #endif @@ -126,18 +128,18 @@ #endif if (m_viewPlayFrame) { - delete m_viewPlayFrame; - m_viewPlayFrame = 0; + delete m_viewPlayFrame; + m_viewPlayFrame = 0; } if (!m_container) return; Layer *layer = dynamic_cast(m_container); if (layer) { - disconnect(layer, SIGNAL(modelReplaced()), + disconnect(layer, SIGNAL(modelReplaced()), this, SLOT(populateViewPlayFrame())); - connect(layer, SIGNAL(modelReplaced()), - this, SLOT(populateViewPlayFrame())); + connect(layer, SIGNAL(modelReplaced()), + this, SLOT(populateViewPlayFrame())); } PlayParameters *params = m_container->getPlayParameters(); @@ -147,7 +149,7 @@ m_viewPlayFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); m_mainBox->addWidget(m_viewPlayFrame); - QHBoxLayout *layout = new QHBoxLayout; + QGridLayout *layout = new QGridLayout; m_viewPlayFrame->setLayout(layout); layout->setMargin(layout->margin() / 2); @@ -156,109 +158,75 @@ SVDEBUG << "PropertyBox::populateViewPlayFrame: container " << m_container << " (name " << m_container->getPropertyContainerName() << ") params " << params << endl; #endif + QSize buttonSize = WidgetScale::scaleQSize(QSize(26, 26)); + int col = 0; + + if (params) { + + m_playButton = new NotifyingToolButton; + m_playButton->setCheckable(true); + m_playButton->setIcon(IconLoader().load("speaker")); + m_playButton->setToolTip(tr("Click to toggle playback")); + m_playButton->setChecked(!params->isPlayMuted()); + m_playButton->setFixedSize(buttonSize); + connect(m_playButton, SIGNAL(toggled(bool)), + this, SLOT(playAudibleButtonChanged(bool))); + connect(m_playButton, SIGNAL(mouseEntered()), + this, SLOT(mouseEnteredWidget())); + connect(m_playButton, SIGNAL(mouseLeft()), + this, SLOT(mouseLeftWidget())); + connect(params, SIGNAL(playAudibleChanged(bool)), + this, SLOT(playAudibleChanged(bool))); + + LevelPanToolButton *levelPan = new LevelPanToolButton; + levelPan->setFixedSize(buttonSize); + levelPan->setImageSize((buttonSize.height() * 3) / 4); + layout->addWidget(levelPan, 0, col++, Qt::AlignCenter); + connect(levelPan, SIGNAL(levelChanged(float)), + this, SLOT(playGainControlChanged(float))); + connect(levelPan, SIGNAL(panChanged(float)), + this, SLOT(playPanControlChanged(float))); + connect(params, SIGNAL(playGainChanged(float)), + levelPan, SLOT(setLevel(float))); + connect(params, SIGNAL(playPanChanged(float)), + levelPan, SLOT(setPan(float))); + connect(levelPan, SIGNAL(mouseEntered()), + this, SLOT(mouseEnteredWidget())); + connect(levelPan, SIGNAL(mouseLeft()), + this, SLOT(mouseLeftWidget())); + + layout->addWidget(m_playButton, 0, col++, Qt::AlignCenter); + + if (params->getPlayClipId() != "") { + NotifyingToolButton *playParamButton = new NotifyingToolButton; + playParamButton->setObjectName("playParamButton"); + playParamButton->setIcon(IconLoader().load("faders")); + playParamButton->setFixedSize(buttonSize); + layout->addWidget(playParamButton, 0, col++, Qt::AlignCenter); + connect(playParamButton, SIGNAL(clicked()), + this, SLOT(editPlayParameters())); + connect(playParamButton, SIGNAL(mouseEntered()), + this, SLOT(mouseEnteredWidget())); + connect(playParamButton, SIGNAL(mouseLeft()), + this, SLOT(mouseLeftWidget())); + } + } + + layout->setColumnStretch(col++, 10); + if (layer) { - QLabel *showLabel = new QLabel(tr("Show")); - layout->addWidget(showLabel); - layout->setAlignment(showLabel, Qt::AlignVCenter); - m_showButton = new LEDButton(Qt::blue); - layout->addWidget(m_showButton); - connect(m_showButton, SIGNAL(stateChanged(bool)), - this, SIGNAL(showLayer(bool))); + QLabel *showLabel = new QLabel(tr("Show")); + layout->addWidget(showLabel, 0, col++, Qt::AlignVCenter | Qt::AlignRight); + + m_showButton = new LEDButton(palette().highlight().color()); + layout->addWidget(m_showButton, 0, col++, Qt::AlignVCenter | Qt::AlignLeft); + connect(m_showButton, SIGNAL(stateChanged(bool)), + this, SIGNAL(showLayer(bool))); connect(m_showButton, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); connect(m_showButton, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - layout->setAlignment(m_showButton, Qt::AlignVCenter); - } - - if (params) { - - QLabel *playLabel = new QLabel(tr("Play")); - layout->addWidget(playLabel); - layout->setAlignment(playLabel, Qt::AlignVCenter); - - m_playButton = new LEDButton(Qt::darkGreen); - m_playButton->setState(!params->isPlayMuted()); - layout->addWidget(m_playButton); - connect(m_playButton, SIGNAL(stateChanged(bool)), - this, SLOT(playAudibleButtonChanged(bool))); - connect(m_playButton, SIGNAL(mouseEntered()), - this, SLOT(mouseEnteredWidget())); - connect(m_playButton, SIGNAL(mouseLeft()), - this, SLOT(mouseLeftWidget())); - connect(params, SIGNAL(playAudibleChanged(bool)), - this, SLOT(playAudibleChanged(bool))); - layout->setAlignment(m_playButton, Qt::AlignVCenter); - - layout->insertStretch(-1, 10); - - if (params->getPlayClipId() != "") { - QPushButton *playParamButton = - new QPushButton(QIcon(":icons/faders.png"), ""); - playParamButton->setFixedWidth(24); - playParamButton->setFixedHeight(24); - layout->addWidget(playParamButton); - connect(playParamButton, SIGNAL(clicked()), - this, SLOT(editPlayParameters())); - } - - AudioDial *gainDial = new AudioDial; - layout->addWidget(gainDial); - gainDial->setMeterColor(Qt::darkRed); - gainDial->setMinimum(-50); - gainDial->setMaximum(50); - gainDial->setPageStep(1); - gainDial->setFixedWidth(24); - gainDial->setFixedHeight(24); - gainDial->setNotchesVisible(false); - gainDial->setObjectName(tr("Playback Gain")); - gainDial->setRangeMapper(new LinearRangeMapper - (-50, 50, -25, 25, tr("dB"))); - gainDial->setDefaultValue(0); - gainDial->setShowToolTip(true); - connect(gainDial, SIGNAL(valueChanged(int)), - this, SLOT(playGainDialChanged(int))); - connect(params, SIGNAL(playGainChanged(float)), - this, SLOT(playGainChanged(float))); - connect(this, SIGNAL(changePlayGainDial(int)), - gainDial, SLOT(setValue(int))); - connect(gainDial, SIGNAL(mouseEntered()), - this, SLOT(mouseEnteredWidget())); - connect(gainDial, SIGNAL(mouseLeft()), - this, SLOT(mouseLeftWidget())); - playGainChanged(params->getPlayGain()); - layout->setAlignment(gainDial, Qt::AlignVCenter); - - AudioDial *panDial = new AudioDial; - layout->addWidget(panDial); - panDial->setMeterColor(Qt::darkGreen); - panDial->setMinimum(-50); - panDial->setMaximum(50); - panDial->setPageStep(1); - panDial->setFixedWidth(24); - panDial->setFixedHeight(24); - panDial->setNotchesVisible(false); - panDial->setToolTip(tr("Playback Pan / Balance")); - panDial->setDefaultValue(0); - panDial->setObjectName(tr("Playback Pan / Balance")); - panDial->setShowToolTip(true); - connect(panDial, SIGNAL(valueChanged(int)), - this, SLOT(playPanDialChanged(int))); - connect(params, SIGNAL(playPanChanged(float)), - this, SLOT(playPanChanged(float))); - connect(this, SIGNAL(changePlayPanDial(int)), - panDial, SLOT(setValue(int))); - connect(panDial, SIGNAL(mouseEntered()), - this, SLOT(mouseEnteredWidget())); - connect(panDial, SIGNAL(mouseLeft()), - this, SLOT(mouseLeftWidget())); - playPanChanged(params->getPlayPan()); - layout->setAlignment(panDial, Qt::AlignVCenter); - - } else { - - layout->insertStretch(-1, 10); } } @@ -273,7 +241,7 @@ value = m_container->getPropertyRangeAndValue(name, &min, &max, &deflt); bool have = (m_propertyControllers.find(name) != - m_propertyControllers.end()); + m_propertyControllers.end()); QString groupName = m_container->getPropertyGroupName(name); QString propertyLabel = m_container->getPropertyLabel(name); @@ -281,91 +249,108 @@ #ifdef DEBUG_PROPERTY_BOX cerr << "PropertyBox[" << this - << "(\"" << m_container->getPropertyContainerName() - << "\")]"; - cerr << "::updatePropertyEditor(\"" << name << "\"):"; - cerr << " value " << value << ", have " << have << ", group \"" - << groupName << "\"" << endl; + << "(\"" << m_container->getPropertyContainerName() + << "\")]"; + cerr << "::updatePropertyEditor(\"" << name << "\", " + << rangeChanged << "):"; + cerr << " type " << type << ", value " << value + << ", have " << have << ", group \"" << groupName << "\"" << endl; #endif - bool inGroup = (groupName != QString()); - + QString groupLabel = groupName; + if (groupName == QString()) { + groupName = "ungrouped: " + name; // not tr(), this is internal id + groupLabel = propertyLabel; + } + if (!have) { - if (inGroup) { - if (m_groupLayouts.find(groupName) == m_groupLayouts.end()) { -#ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox: adding label \"" << groupName << "\" and frame for group for \"" << name << "\"" << endl; + if (m_groupLayouts.find(groupName) == m_groupLayouts.end()) { + QWidget *labelWidget = new QLabel(groupLabel, m_mainWidget); + m_layout->addWidget(labelWidget, row, 0); + QWidget *frame = new QWidget(m_mainWidget); + frame->setMinimumSize(WidgetScale::scaleQSize(QSize(1, 24))); + m_groupLayouts[groupName] = new QGridLayout; +#ifdef Q_OS_MAC + // Seems to be plenty of whitespace already + m_groupLayouts[groupName]->setContentsMargins(0, 0, 0, 0); +#else + // Need a bit of padding on the left + m_groupLayouts[groupName]->setContentsMargins + (WidgetScale::scalePixelSize(10), 0, 0, 0); #endif - m_layout->addWidget(new QLabel(groupName, m_mainWidget), row, 0); - QFrame *frame = new QFrame(m_mainWidget); - m_layout->addWidget(frame, row, 1, 1, 2); - m_groupLayouts[groupName] = new QGridLayout; - m_groupLayouts[groupName]->setMargin(0); - frame->setLayout(m_groupLayouts[groupName]); - } - } else { -#ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox: adding label \"" << propertyLabel << "\"" << endl; -#endif - m_layout->addWidget(new QLabel(propertyLabel, m_mainWidget), row, 0); - } + frame->setLayout(m_groupLayouts[groupName]); + m_layout->addWidget(frame, row, 1, 1, 2); + m_layout->setColumnStretch(1, 10); + } } + QGridLayout *groupLayout = m_groupLayouts[groupName]; + +#ifdef DEBUG_PROPERTY_BOX + cerr << "groupName becomes \"" << groupName << "\", groupLabel = \"" + << groupLabel << "\", groupLayout = " << groupLayout << endl; +#endif + + assert(groupLayout); + + QWidget *existing = m_propertyControllers[name]; + switch (type) { case PropertyContainer::ToggleProperty: { - QAbstractButton *button = 0; + QAbstractButton *button; - if (have) { - button = dynamic_cast(m_propertyControllers[name]); - assert(button); - } else { + if (!(button = qobject_cast(existing))) { #ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox: creating new checkbox" << endl; + cerr << "PropertyBox: creating new checkbox" << endl; #endif if (iconName != "") { +#ifdef Q_OS_MAC + button = new NotifyingToolButton(); +#else button = new NotifyingPushButton(); +#endif button->setCheckable(true); QIcon icon(IconLoader().load(iconName)); button->setIcon(icon); button->setObjectName(name); - button->setFixedSize(QSize(18, 18)); + button->setFixedSize(WidgetScale::scaleQSize(QSize(18, 18))); } else { button = new NotifyingCheckBox(); button->setObjectName(name); } - connect(button, SIGNAL(toggled(bool)), - this, SLOT(propertyControllerChanged(bool))); + connect(button, SIGNAL(toggled(bool)), + this, SLOT(propertyControllerChanged(bool))); connect(button, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); connect(button, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - if (inGroup) { - button->setToolTip(propertyLabel); - m_groupLayouts[groupName]->addWidget - (button, 0, m_groupLayouts[groupName]->columnCount()); - } else { - m_layout->addWidget(button, row, 1, 1, 2); - } - m_propertyControllers[name] = button; - } + button->setToolTip(propertyLabel); + + if (existing) { + groupLayout->replaceWidget(existing, button); + delete existing; + } else { + groupLayout->addWidget(button, 0, groupLayout->columnCount()); + } + + m_propertyControllers[name] = button; + } if (button->isChecked() != (value > 0)) { - button->blockSignals(true); - button->setChecked(value > 0); - button->blockSignals(false); - } - break; + button->blockSignals(true); + button->setChecked(value > 0); + button->blockSignals(false); + } + break; } case PropertyContainer::RangeProperty: { - AudioDial *dial; + AudioDial *dial; - if (have) { - dial = dynamic_cast(m_propertyControllers[name]); - assert(dial); + if ((dial = qobject_cast(existing))) { if (rangeChanged) { dial->blockSignals(true); dial->setMinimum(min); @@ -373,72 +358,139 @@ dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name)); dial->blockSignals(false); } - - } else { + } else { #ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox: creating new dial" << endl; + cerr << "PropertyBox: creating new dial" << endl; #endif - dial = new AudioDial(); - dial->setObjectName(name); - dial->setMinimum(min); - dial->setMaximum(max); - dial->setPageStep(1); - dial->setNotchesVisible((max - min) <= 12); + dial = new AudioDial(); + dial->setObjectName(name); + dial->setMinimum(min); + dial->setMaximum(max); + dial->setPageStep(1); + dial->setNotchesVisible((max - min) <= 12); // important to set the range mapper before the default, // because the range mapper is used to map the default dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name)); - dial->setDefaultValue(deflt); + dial->setDefaultValue(deflt); dial->setShowToolTip(true); - connect(dial, SIGNAL(valueChanged(int)), - this, SLOT(propertyControllerChanged(int))); + connect(dial, SIGNAL(valueChanged(int)), + this, SLOT(propertyControllerChanged(int))); connect(dial, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); connect(dial, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - if (inGroup) { - dial->setFixedWidth(24); - dial->setFixedHeight(24); - m_groupLayouts[groupName]->addWidget - (dial, 0, m_groupLayouts[groupName]->columnCount()); - } else { - dial->setFixedWidth(32); - dial->setFixedHeight(32); - m_layout->addWidget(dial, row, 1); - QLabel *label = new QLabel(m_mainWidget); - connect(dial, SIGNAL(valueChanged(int)), - label, SLOT(setNum(int))); - label->setNum(value); - m_layout->addWidget(label, row, 2); - } + dial->setFixedWidth(WidgetScale::scalePixelSize(24)); + dial->setFixedHeight(WidgetScale::scalePixelSize(24)); - m_propertyControllers[name] = dial; - } + if (existing) { + groupLayout->replaceWidget(existing, dial); + delete existing; + } else { + groupLayout->addWidget(dial, 0, groupLayout->columnCount()); + } - if (dial->value() != value) { - dial->blockSignals(true); - dial->setValue(value); - dial->blockSignals(false); - } - break; + m_propertyControllers[name] = dial; + } + + if (dial->value() != value) { + dial->blockSignals(true); + dial->setValue(value); + dial->blockSignals(false); + } + break; } + case PropertyContainer::ColourProperty: + { + ColourComboBox *cb; + + if (!(cb = qobject_cast(existing))) { + +#ifdef DEBUG_PROPERTY_BOX + cerr << "PropertyBox: creating new colour combobox" << endl; +#endif + cb = new ColourComboBox(true); + cb->setObjectName(name); + + connect(cb, SIGNAL(colourChanged(int)), + this, SLOT(propertyControllerChanged(int))); + connect(cb, SIGNAL(mouseEntered()), + this, SLOT(mouseEnteredWidget())); + connect(cb, SIGNAL(mouseLeft()), + this, SLOT(mouseLeftWidget())); + + cb->setToolTip(propertyLabel); + + if (existing) { + groupLayout->replaceWidget(existing, cb); + delete existing; + } else { + groupLayout->addWidget(cb, 0, groupLayout->columnCount()); + } + + m_propertyControllers[name] = cb; + } + + if (cb->currentIndex() != value) { + cb->blockSignals(true); + cb->setCurrentIndex(value); + cb->blockSignals(false); + } + + break; + } + + case PropertyContainer::ColourMapProperty: + { + ColourMapComboBox *cb; + + if (!(cb = qobject_cast(existing))) { +#ifdef DEBUG_PROPERTY_BOX + cerr << "PropertyBox: creating new colourmap combobox" << endl; +#endif + cb = new ColourMapComboBox(false); + cb->setObjectName(name); + + connect(cb, SIGNAL(colourMapChanged(int)), + this, SLOT(propertyControllerChanged(int))); + connect(cb, SIGNAL(mouseEntered()), + this, SLOT(mouseEnteredWidget())); + connect(cb, SIGNAL(mouseLeft()), + this, SLOT(mouseLeftWidget())); + + cb->setToolTip(propertyLabel); + + if (existing) { + groupLayout->replaceWidget(existing, cb); + delete existing; + } else { + groupLayout->addWidget(cb, 0, groupLayout->columnCount()); + } + + m_propertyControllers[name] = cb; + } + + if (cb->currentIndex() != value) { + cb->blockSignals(true); + cb->setCurrentIndex(value); + cb->blockSignals(false); + } + + break; + } + case PropertyContainer::ValueProperty: case PropertyContainer::UnitsProperty: - case PropertyContainer::ColourProperty: { - NotifyingComboBox *cb; + NotifyingComboBox *cb; - if (have) { - cb = dynamic_cast(m_propertyControllers[name]); - assert(cb); - } else { + if (!(cb = qobject_cast(existing))) { #ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox: creating new combobox" << endl; + cerr << "PropertyBox: creating new combobox" << endl; #endif - - cb = new NotifyingComboBox(); - cb->setObjectName(name); + cb = new NotifyingComboBox(); + cb->setObjectName(name); cb->setDuplicatesEnabled(false); } @@ -463,7 +515,7 @@ } } - } else if (type == PropertyContainer::UnitsProperty) { + } else { // PropertyContainer::UnitsProperty QStringList units = UnitDatabase::getInstance()->getKnownUnits(); for (int i = 0; i < units.size(); ++i) { @@ -471,50 +523,27 @@ } cb->setEditable(true); - - } else { // ColourProperty - - //!!! should be a proper colour combobox class that - // manages its own Add New Colour entry... - - int size = (QFontMetrics(QFont()).height() * 2) / 3; - if (size < 12) size = 12; - - ColourDatabase *db = ColourDatabase::getInstance(); - for (int i = 0; i < db->getColourCount(); ++i) { - QString name = db->getColourName(i); - cb->addItem(db->getExamplePixmap(i, QSize(size, size)), name); - } - cb->addItem(tr("Add New Colour...")); - } - - cb->blockSignals(false); - if (cb->count() < 20 && cb->count() > cb->maxVisibleItems()) { - cb->setMaxVisibleItems(cb->count()); } } if (!have) { - connect(cb, SIGNAL(activated(int)), - this, SLOT(propertyControllerChanged(int))); + connect(cb, SIGNAL(activated(int)), + this, SLOT(propertyControllerChanged(int))); connect(cb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); connect(cb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - if (inGroup) { - cb->setToolTip(propertyLabel); - m_groupLayouts[groupName]->addWidget - (cb, 0, m_groupLayouts[groupName]->columnCount()); - } else { - m_layout->addWidget(cb, row, 1, 1, 2); - } - m_propertyControllers[name] = cb; - } + cb->setToolTip(propertyLabel); + groupLayout->addWidget(cb, 0, groupLayout->columnCount()); + m_propertyControllers[name] = cb; + } else if (existing != cb) { + groupLayout->replaceWidget(existing, cb); + delete existing; + } cb->blockSignals(true); - if (type == PropertyContainer::ValueProperty || - type == PropertyContainer::ColourProperty) { + if (type == PropertyContainer::ValueProperty) { if (cb->currentIndex() != value) { cb->setCurrentIndex(value); } @@ -531,19 +560,12 @@ } cb->blockSignals(false); -#ifdef Q_OS_MAC - // Crashes on startup without this, for some reason; also - // prevents combo boxes from getting weirdly squished - // vertically - cb->setMinimumSize(QSize(10, cb->font().pixelSize() * 2)); -#endif - - break; + break; } case PropertyContainer::InvalidProperty: default: - break; + break; } } @@ -562,7 +584,7 @@ blockSignals(true); for (i = 0; i < properties.size(); ++i) { - updatePropertyEditor(properties[i]); + updatePropertyEditor(properties[i]); } blockSignals(false); @@ -575,7 +597,7 @@ PropertyContainer::PropertyList properties = m_container->getProperties(); for (size_t i = 0; i < properties.size(); ++i) { - updatePropertyEditor(properties[i], true); + updatePropertyEditor(properties[i], true); } blockSignals(false); @@ -605,22 +627,6 @@ } void -PropertyBox::colourDatabaseChanged() -{ - blockSignals(true); - - PropertyContainer::PropertyList properties = m_container->getProperties(); - for (size_t i = 0; i < properties.size(); ++i) { - if (m_container->getPropertyType(properties[i]) == - PropertyContainer::ColourProperty) { - updatePropertyEditor(properties[i], true); - } - } - - blockSignals(false); -} - -void PropertyBox::propertyControllerChanged(bool on) { propertyControllerChanged(on ? 1 : 0); @@ -633,7 +639,7 @@ QString name = obj->objectName(); #ifdef DEBUG_PROPERTY_BOX - SVDEBUG << "PropertyBox::propertyControllerChanged(" << name << ", " << value << ")" << endl; + SVDEBUG << "PropertyBox::propertyControllerChanged(" << name << ", " << value << ")" << endl; #endif PropertyContainer::PropertyType type = m_container->getPropertyType(name); @@ -642,27 +648,16 @@ if (type == PropertyContainer::UnitsProperty) { - NotifyingComboBox *cb = dynamic_cast(obj); + NotifyingComboBox *cb = qobject_cast(obj); if (cb) { QString unit = cb->currentText(); c = m_container->getSetPropertyCommand (name, UnitDatabase::getInstance()->getUnitId(unit)); } - } else if (type == PropertyContainer::ColourProperty) { - - if (value == int(ColourDatabase::getInstance()->getColourCount())) { - addNewColour(); - if (value == int(ColourDatabase::getInstance()->getColourCount())) { - propertyContainerPropertyChanged(m_container); - return; - } - } - c = m_container->getSetPropertyCommand(name, value); - } else if (type != PropertyContainer::InvalidProperty) { - c = m_container->getSetPropertyCommand(name, value); + c = m_container->getSetPropertyCommand(name, value); } if (c) CommandHistory::getInstance()->addCommand(c, true, true); @@ -671,27 +666,9 @@ } void -PropertyBox::addNewColour() -{ - QColor newColour = QColorDialog::getColor(); - if (!newColour.isValid()) return; - - ColourNameDialog dialog(tr("Name New Colour"), - tr("Enter a name for the new colour:"), - newColour, newColour.name(), this); - dialog.showDarkBackgroundCheckbox(tr("Prefer black background for this colour")); - if (dialog.exec() == QDialog::Accepted) { - //!!! command - ColourDatabase *db = ColourDatabase::getInstance(); - int index = db->addColour(newColour, dialog.getColourName()); - db->setUseDarkBackground(index, dialog.isDarkBackgroundChecked()); - } -} - -void PropertyBox::playAudibleChanged(bool audible) { - m_playButton->setState(audible); + m_playButton->setChecked(audible); } void @@ -707,26 +684,15 @@ CommandHistory::getInstance()->addCommand(command, true, true); } } - -void -PropertyBox::playGainChanged(float gain) -{ - int dialValue = int(lrint(log10(gain) * 20.0)); - if (dialValue < -50) dialValue = -50; - if (dialValue > 50) dialValue = 50; - emit changePlayGainDial(dialValue); -} void -PropertyBox::playGainDialChanged(int dialValue) +PropertyBox::playGainControlChanged(float gain) { QObject *obj = sender(); PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - float gain = float(pow(10, float(dialValue) / 20.0)); - if (params->getPlayGain() != gain) { PlayParameterRepository::EditCommand *command = new PlayParameterRepository::EditCommand(params); @@ -736,28 +702,15 @@ updateContextHelp(obj); } - -void -PropertyBox::playPanChanged(float pan) -{ - int dialValue = int(lrint(pan * 50.0)); - if (dialValue < -50) dialValue = -50; - if (dialValue > 50) dialValue = 50; - emit changePlayPanDial(dialValue); -} void -PropertyBox::playPanDialChanged(int dialValue) +PropertyBox::playPanControlChanged(float pan) { QObject *obj = sender(); PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - float pan = float(dialValue) / 50.f; - if (pan < -1.f) pan = -1.f; - if (pan > 1.f) pan = 1.f; - if (params->getPlayPan() != pan) { PlayParameterRepository::EditCommand *command = new PlayParameterRepository::EditCommand(params); @@ -842,17 +795,34 @@ void PropertyBox::updateContextHelp(QObject *o) { - QWidget *w = dynamic_cast(o); + QWidget *w = qobject_cast(o); if (!w) return; if (!m_container) return; QString cname = m_container->getPropertyContainerName(); if (cname == "") return; + LevelPanToolButton *lp = qobject_cast(w); + if (lp) { + emit contextHelpChanged(tr("Adjust playback level and pan of %1").arg(cname)); + return; + } + QString wname = w->objectName(); + if (wname == "playParamButton") { + PlayParameters *params = m_container->getPlayParameters(); + if (params) { + emit contextHelpChanged + (tr("Change sound used for playback (currently \"%1\")") + .arg(params->getPlayClipId())); + return; + } + } + QString extraText; - AudioDial *dial = dynamic_cast(w); + + AudioDial *dial = qobject_cast(w); if (dial) { double mv = dial->mappedValue(); QString unit = ""; @@ -870,7 +840,7 @@ emit contextHelpChanged(tr("Toggle Playback of %1").arg(cname)); } else if (wname == "") { return; - } else if (dynamic_cast(w)) { + } else if (qobject_cast(w)) { emit contextHelpChanged(tr("Toggle %1 property of %2") .arg(wname).arg(cname)); } else { diff -r 57d192e26331 -r 13d9b422f7fe widgets/PropertyBox.h --- a/widgets/PropertyBox.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/PropertyBox.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _PROPERTY_BOX_H_ -#define _PROPERTY_BOX_H_ +#ifndef SV_PROPERTY_BOX_H +#define SV_PROPERTY_BOX_H #include "base/PropertyContainer.h" @@ -27,6 +27,8 @@ class QVBoxLayout; class QLabel; class LEDButton; +class QToolButton; +class NotifyingPushButton; class PropertyBox : public QFrame { @@ -39,8 +41,6 @@ PropertyContainer *getContainer() { return m_container; } signals: - void changePlayGainDial(int); - void changePlayPanDial(int); void showLayer(bool); void contextHelpChanged(const QString &); @@ -56,15 +56,12 @@ void playAudibleChanged(bool); void playAudibleButtonChanged(bool); - void playGainChanged(float); - void playGainDialChanged(int); - void playPanChanged(float); - void playPanDialChanged(int); + void playGainControlChanged(float); + void playPanControlChanged(float); void populateViewPlayFrame(); void unitDatabaseChanged(); - void colourDatabaseChanged(); void editPlayParameters(); @@ -75,7 +72,6 @@ void updatePropertyEditor(PropertyContainer::PropertyName, bool rangeChanged = false); void updateContextHelp(QObject *o); - void addNewColour(); QLabel *m_nameWidget; QWidget *m_mainWidget; @@ -84,7 +80,7 @@ QFrame *m_viewPlayFrame; QVBoxLayout *m_mainBox; LEDButton *m_showButton; - LEDButton *m_playButton; + QToolButton *m_playButton; std::map m_groupLayouts; std::map m_propertyControllers; }; diff -r 57d192e26331 -r 13d9b422f7fe widgets/PropertyStack.cpp --- a/widgets/PropertyStack.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/PropertyStack.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -25,6 +25,8 @@ #include "widgets/CommandHistory.h" #include "layer/ShowLayerCommand.h" +#include "WidgetScale.h" + #include #include @@ -45,34 +47,32 @@ setTabBar(bar); -#if (QT_VERSION >= 0x0402) setElideMode(Qt::ElideNone); tabBar()->setUsesScrollButtons(true); - tabBar()->setIconSize(QSize(16, 16)); -#endif + tabBar()->setIconSize(WidgetScale::scaleQSize(QSize(16, 16))); repopulate(); connect(this, SIGNAL(currentChanged(int)), - this, SLOT(selectedContainerChanged(int))); + this, SLOT(selectedContainerChanged(int))); connect(m_client, SIGNAL(propertyContainerAdded(PropertyContainer *)), - this, SLOT(propertyContainerAdded(PropertyContainer *))); + this, SLOT(propertyContainerAdded(PropertyContainer *))); connect(m_client, SIGNAL(propertyContainerRemoved(PropertyContainer *)), - this, SLOT(propertyContainerRemoved(PropertyContainer *))); + this, SLOT(propertyContainerRemoved(PropertyContainer *))); connect(m_client, SIGNAL(propertyContainerPropertyChanged(PropertyContainer *)), - this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); + this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); connect(m_client, SIGNAL(propertyContainerPropertyRangeChanged(PropertyContainer *)), - this, SLOT(propertyContainerPropertyRangeChanged(PropertyContainer *))); + this, SLOT(propertyContainerPropertyRangeChanged(PropertyContainer *))); connect(m_client, SIGNAL(propertyContainerNameChanged(PropertyContainer *)), - this, SLOT(propertyContainerNameChanged(PropertyContainer *))); + this, SLOT(propertyContainerNameChanged(PropertyContainer *))); connect(this, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)), - m_client, SLOT(propertyContainerSelected(View *, PropertyContainer *))); + m_client, SLOT(propertyContainerSelected(View *, PropertyContainer *))); } PropertyStack::~PropertyStack() @@ -89,27 +89,27 @@ #endif while (count() > 0) { - removeTab(0); + removeTab(0); } for (size_t i = 0; i < m_boxes.size(); ++i) { - delete m_boxes[i]; + delete m_boxes[i]; } m_boxes.clear(); for (int i = 0; i < m_client->getPropertyContainerCount(); ++i) { - PropertyContainer *container = m_client->getPropertyContainer(i); - QString name = container->getPropertyContainerName(); - + PropertyContainer *container = m_client->getPropertyContainer(i); + QString name = container->getPropertyContainerName(); + #ifdef DEBUG_PROPERTY_STACK cerr << "PropertyStack[" << this << "]::repopulate: client " << m_client << " returns container " << container << " (name " << name << ") at position " << i << endl; #endif - PropertyBox *box = new PropertyBox(container); + PropertyBox *box = new PropertyBox(container); - connect(box, SIGNAL(showLayer(bool)), this, SLOT(showLayer(bool))); + connect(box, SIGNAL(showLayer(bool)), this, SLOT(showLayer(bool))); connect(box, SIGNAL(contextHelpChanged(const QString &)), this, SIGNAL(contextHelpChanged(const QString &))); @@ -131,20 +131,20 @@ bool nameDiffers = (name != shortName); shortName = QString("&%1 %2").arg(i + 1).arg(shortName); - QString iconName = container->getPropertyContainerIconName(); + QString iconName = container->getPropertyContainerIconName(); QIcon icon(IconLoader().load(iconName)); - if (icon.isNull()) { - addTab(box, shortName); + if (icon.isNull()) { + addTab(box, shortName); if (nameDiffers) { setTabToolTip(i, name); } - } else { - addTab(box, icon, QString("&%1").arg(i + 1)); - setTabToolTip(i, name); - } + } else { + addTab(box, icon, QString("&%1").arg(i + 1)); + setTabToolTip(i, name); + } - m_boxes.push_back(box); + m_boxes.push_back(box); } blockSignals(false); @@ -154,8 +154,8 @@ PropertyStack::containsContainer(PropertyContainer *pc) const { for (int i = 0; i < m_client->getPropertyContainerCount(); ++i) { - PropertyContainer *container = m_client->getPropertyContainer(i); - if (pc == container) return true; + PropertyContainer *container = m_client->getPropertyContainer(i); + if (pc == container) return true; } return false; @@ -173,8 +173,8 @@ // box list, not in the view. for (int i = 0; in_range_for(m_boxes, i); ++i) { - PropertyContainer *container = m_boxes[i]->getContainer(); - if (pc == container) { + PropertyContainer *container = m_boxes[i]->getContainer(); + if (pc == container) { return i; } } @@ -201,13 +201,13 @@ { Layer *layer = dynamic_cast(pc); for (unsigned int i = 0; i < m_boxes.size(); ++i) { - if (pc == m_boxes[i]->getContainer()) { - m_boxes[i]->propertyContainerPropertyChanged(pc); + if (pc == m_boxes[i]->getContainer()) { + m_boxes[i]->propertyContainerPropertyChanged(pc); if (layer) { m_boxes[i]->layerVisibilityChanged (!layer->isLayerDormant(m_client)); } - } + } } } @@ -215,9 +215,9 @@ PropertyStack::propertyContainerPropertyRangeChanged(PropertyContainer *pc) { for (unsigned int i = 0; i < m_boxes.size(); ++i) { - if (pc == m_boxes[i]->getContainer()) { - m_boxes[i]->propertyContainerPropertyRangeChanged(pc); - } + if (pc == m_boxes[i]->getContainer()) { + m_boxes[i]->propertyContainerPropertyRangeChanged(pc); + } } } @@ -234,15 +234,15 @@ QObject *obj = sender(); for (unsigned int i = 0; i < m_boxes.size(); ++i) { - if (obj == m_boxes[i]) { - Layer *layer = dynamic_cast(m_boxes[i]->getContainer()); - if (layer) { + if (obj == m_boxes[i]) { + Layer *layer = dynamic_cast(m_boxes[i]->getContainer()); + if (layer) { CommandHistory::getInstance()->addCommand (new ShowLayerCommand(m_client, layer, show, tr("Change Layer Visibility"))); - return; - } - } + return; + } + } } } diff -r 57d192e26331 -r 13d9b422f7fe widgets/Thumbwheel.cpp --- a/widgets/Thumbwheel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/Thumbwheel.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -29,7 +29,7 @@ #include Thumbwheel::Thumbwheel(Qt::Orientation orientation, - QWidget *parent) : + QWidget *parent) : QWidget(parent), m_min(0), m_max(100), @@ -244,9 +244,9 @@ if (step == 0) step = 1; if (up) { - setValue(m_value + step); + setValue(m_value + step); } else { - setValue(m_value - step); + setValue(m_value - step); } emit valueChanged(getValue()); @@ -426,15 +426,13 @@ void Thumbwheel::wheelEvent(QWheelEvent *e) { - int step = int(lrintf(m_speed)); - if (step == 0) step = 1; + int delta = m_wheelCounter.count(e); - if (e->delta() > 0) { - setValue(m_value + step); - } else { - setValue(m_value - step); + if (delta == 0) { + return; } - + + setValue(m_value + delta); emit valueChanged(getValue()); } @@ -445,35 +443,36 @@ if (!m_cache.isNull()) { QPainter paint(this); - paint.drawImage(0, 0, m_cache); + paint.drawImage(rect(), m_cache, m_cache.rect()); return; } Profiler profiler2("Thumbwheel::paintEvent (no cache)"); - m_cache = QImage(size(), QImage::Format_ARGB32); + QSize imageSize = size() * devicePixelRatio(); + m_cache = QImage(imageSize, QImage::Format_ARGB32); m_cache.fill(Qt::transparent); - int bw = 3; + int w = m_cache.width(); + int h = m_cache.height(); + int bw = 3; // border width QRect subclip; if (m_orientation == Qt::Horizontal) { - subclip = QRect(bw, bw+1, width() - bw*2, height() - bw*2 - 2); + subclip = QRect(bw, bw+1, w - bw*2, h - bw*2 - 2); } else { - subclip = QRect(bw+1, bw, width() - bw*2 - 2, height() - bw*2); + subclip = QRect(bw+1, bw, w - bw*2 - 2, h - bw*2); } QPainter paint(&m_cache); - paint.setClipRect(rect()); + paint.setClipRect(m_cache.rect()); paint.fillRect(subclip, palette().background().color()); paint.setRenderHint(QPainter::Antialiasing, true); - double w = width(); double w0 = 0.5; double w1 = w - 0.5; - double h = height(); double h0 = 0.5; double h1 = h - 0.5; @@ -508,13 +507,13 @@ // cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << endl; - w = (m_orientation == Qt::Horizontal ? width() : height()) - bw*2; + int ww = (m_orientation == Qt::Horizontal ? w : h) - bw*2; // wheel width // total number of notches on the entire wheel int notches = 25; // radius of the wheel including invisible part - int radius = int(w / 2 + 2); + int radius = int(ww / 2 + 2); for (int i = 0; i < notches; ++i) { @@ -525,13 +524,13 @@ double depth = cos((a0 + a2) / 2); if (depth < 0) continue; - double x0 = radius * sin(a0) + w/2; - double x1 = radius * sin(a1) + w/2; - double x2 = radius * sin(a2) + w/2; - if (x2 < 0 || x0 > w) continue; + double x0 = radius * sin(a0) + ww/2; + double x1 = radius * sin(a1) + ww/2; + double x2 = radius * sin(a2) + ww/2; + if (x2 < 0 || x0 > ww) continue; if (x0 < 0) x0 = 0; - if (x2 > w) x2 = w; + if (x2 > ww) x2 = ww; x0 += bw; x1 += bw; @@ -557,10 +556,10 @@ } if (m_orientation == Qt::Horizontal) { - paint.drawRect(QRectF(x1, height() - (height() - bw*2) * prop - bw, - x2 - x1, height() * prop)); + paint.drawRect(QRectF(x1, h - (h - bw*2) * prop - bw, + x2 - x1, h * prop)); } else { - paint.drawRect(QRectF(bw, x1, (width() - bw*2) * prop, x2 - x1)); + paint.drawRect(QRectF(bw, x1, (w - bw*2) * prop, x2 - x1)); } } @@ -568,14 +567,14 @@ paint.setBrush(palette().background().color()); if (m_orientation == Qt::Horizontal) { - paint.drawRect(QRectF(x0, bw, x1 - x0, height() - bw*2)); + paint.drawRect(QRectF(x0, bw, x1 - x0, h - bw*2)); } else { - paint.drawRect(QRectF(bw, x0, width() - bw*2, x1 - x0)); + paint.drawRect(QRectF(bw, x0, w - bw*2, x1 - x0)); } } QPainter paint2(this); - paint2.drawImage(0, 0, m_cache); + paint2.drawImage(rect(), m_cache, m_cache.rect()); } QSize diff -r 57d192e26331 -r 13d9b422f7fe widgets/Thumbwheel.h --- a/widgets/Thumbwheel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/Thumbwheel.h Mon Sep 17 13:51:31 2018 +0100 @@ -13,14 +13,16 @@ COPYING included with this distribution for more information. */ -#ifndef _THUMBWHEEL_H_ -#define _THUMBWHEEL_H_ +#ifndef SV_THUMBWHEEL_H +#define SV_THUMBWHEEL_H #include #include #include +#include "WheelCounter.h" + class RangeMapper; class Thumbwheel : public QWidget @@ -96,6 +98,7 @@ bool m_showTooltip; RangeMapper *m_rangeMapper; QImage m_cache; + WheelCounter m_wheelCounter; }; #endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/TipDialog.cpp --- a/widgets/TipDialog.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/TipDialog.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -276,10 +276,10 @@ TipDialog::TipFileParser::error(const QXmlParseException &exception) { QString errorString = - QString("ERROR: TipFileParser: %1 at line %2, column %3") - .arg(exception.message()) - .arg(exception.lineNumber()) - .arg(exception.columnNumber()); + QString("ERROR: TipFileParser: %1 at line %2, column %3") + .arg(exception.message()) + .arg(exception.lineNumber()) + .arg(exception.columnNumber()); cerr << errorString << endl; return QXmlDefaultHandler::error(exception); } @@ -288,10 +288,10 @@ TipDialog::TipFileParser::fatalError(const QXmlParseException &exception) { QString errorString = - QString("FATAL ERROR: TipFileParser: %1 at line %2, column %3") - .arg(exception.message()) - .arg(exception.lineNumber()) - .arg(exception.columnNumber()); + QString("FATAL ERROR: TipFileParser: %1 at line %2, column %3") + .arg(exception.message()) + .arg(exception.lineNumber()) + .arg(exception.columnNumber()); cerr << errorString << endl; return QXmlDefaultHandler::fatalError(exception); } diff -r 57d192e26331 -r 13d9b422f7fe widgets/UnitConverter.cpp --- a/widgets/UnitConverter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/widgets/UnitConverter.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -59,7 +59,7 @@ m_freq->setMaximum(1e6); m_freq->setValue(440); connect(m_freq, SIGNAL(valueChanged(double)), - this, SLOT(freqChanged())); + this, SLOT(freqChanged())); // The min and max range values for all the remaining controls are // determined by the min and max Hz above @@ -68,20 +68,20 @@ m_midi->setMinimum(-156); m_midi->setMaximum(203); connect(m_midi, SIGNAL(valueChanged(int)), - this, SLOT(midiChanged())); + this, SLOT(midiChanged())); m_note = new QComboBox; for (int i = 0; i < 12; ++i) { - m_note->addItem(pianoNotes[i]); + m_note->addItem(pianoNotes[i]); } connect(m_note, SIGNAL(currentIndexChanged(int)), - this, SLOT(noteChanged())); + this, SLOT(noteChanged())); m_octave = new QSpinBox; m_octave->setMinimum(-14); m_octave->setMaximum(15); connect(m_octave, SIGNAL(valueChanged(int)), - this, SLOT(octaveChanged())); + this, SLOT(octaveChanged())); m_cents = new QDoubleSpinBox; m_cents->setSuffix(tr(" cents")); @@ -89,7 +89,7 @@ m_cents->setMinimum(-50); m_cents->setMaximum(50); connect(m_cents, SIGNAL(valueChanged(double)), - this, SLOT(centsChanged())); + this, SLOT(centsChanged())); int row = 0; @@ -130,9 +130,9 @@ ++row; grid->addWidget - (new QLabel(tr("Note that only pitches in the range 0 to 127 are valid " - "in the MIDI protocol.")), - row, 0, 1, 9); + (new QLabel(tr("Note that only pitches in the range 0 to 127 are valid " + "in the MIDI protocol.")), + row, 0, 1, 9); ++row; @@ -149,7 +149,7 @@ m_samples->setMaximum(1e8); m_samples->setValue(22050); connect(m_samples, SIGNAL(valueChanged(double)), - this, SLOT(samplesChanged())); + this, SLOT(samplesChanged())); m_period = new QDoubleSpinBox; m_period->setSuffix(QString(" ms")); @@ -158,7 +158,7 @@ m_period->setMaximum(100000); m_period->setValue(500); connect(m_period, SIGNAL(valueChanged(double)), - this, SLOT(periodChanged())); + this, SLOT(periodChanged())); m_bpm = new QDoubleSpinBox; m_bpm->setSuffix(QString(" bpm")); @@ -167,7 +167,7 @@ m_bpm->setMaximum(1e6); m_bpm->setValue(120); connect(m_bpm, SIGNAL(valueChanged(double)), - this, SLOT(bpmChanged())); + this, SLOT(bpmChanged())); m_tempofreq = new QDoubleSpinBox; m_tempofreq->setSuffix(QString(" beats/sec")); @@ -177,24 +177,24 @@ m_tempofreq->setValue(0.5); connect(m_tempofreq, SIGNAL(valueChanged(double)), - this, SLOT(tempofreqChanged())); - + this, SLOT(tempofreqChanged())); + m_samplerate = new QComboBox; QList rates; rates << 8000; for (int i = 1; i <= 16; i *= 2) { - rates << 11025 * i << 12000 * i; + rates << 11025 * i << 12000 * i; } foreach (int r, rates) { - m_samplerate->addItem(QString("%1 Hz").arg(r)); + m_samplerate->addItem(QString("%1 Hz").arg(r)); } connect(m_samplerate, SIGNAL(currentIndexChanged(int)), - this, SLOT(samplerateChanged())); + this, SLOT(samplerateChanged())); m_samplerate->setCurrentText("44100 Hz"); connect(Preferences::getInstance(), - SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); + SIGNAL(propertyChanged(PropertyContainer::PropertyName)), + this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); row = 0; @@ -236,11 +236,11 @@ { box->blockSignals(true); if (value < box->minimum() || value > box->maximum()) { - QPalette p; - p.setColor(QPalette::Text, Qt::red); - box->setPalette(p); + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); } else { - box->setPalette(QPalette()); + box->setPalette(QPalette()); } box->setValue(value); box->blockSignals(false); @@ -251,11 +251,11 @@ { box->blockSignals(true); if (value < box->minimum() || value > box->maximum()) { - QPalette p; - p.setColor(QPalette::Text, Qt::red); - box->setPalette(p); + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); } else { - box->setPalette(QPalette()); + box->setPalette(QPalette()); } box->setValue(value); box->blockSignals(false); @@ -272,11 +272,11 @@ UnitConverter::updatePitchPrefsLabel() { m_pitchPrefsLabel->setText - (tr("With concert-A tuning frequency at %1 Hz, and " - "middle C residing in octave %2.\n" - "(These can be changed in the application preferences.)") - .arg(Preferences::getInstance()->getTuningFrequency()) - .arg(Preferences::getInstance()->getOctaveOfMiddleC())); + (tr("With concert-A tuning frequency at %1 Hz, and " + "middle C residing in octave %2.\n" + "(These can be changed in the application preferences.)") + .arg(Preferences::getInstance()->getTuningFrequency()) + .arg(Preferences::getInstance()->getOctaveOfMiddleC())); } void @@ -296,7 +296,7 @@ UnitConverter::noteChanged() { int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), - m_octave->value()); + m_octave->value()); double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); m_freq->setValue(freq); } @@ -305,7 +305,7 @@ UnitConverter::octaveChanged() { int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), - m_octave->value()); + m_octave->value()); double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); m_freq->setValue(freq); } diff -r 57d192e26331 -r 13d9b422f7fe widgets/WheelCounter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/WheelCounter.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,65 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_WHEEL_COUNTER_H +#define SV_WHEEL_COUNTER_H + +#include + +/** + * Manage the little bit of tedious book-keeping associated with + * translating vertical wheel events into up/down notch counts + */ +class WheelCounter +{ +public: + WheelCounter() : m_pendingWheelAngle(0) { } + + ~WheelCounter() { } + + int count(QWheelEvent *e) { + + e->accept(); + + int delta = e->angleDelta().y(); + if (delta == 0) { + return 0; + } + + if (e->phase() == Qt::ScrollBegin || + std::abs(delta) >= 120 || + (delta > 0 && m_pendingWheelAngle < 0) || + (delta < 0 && m_pendingWheelAngle > 0)) { + m_pendingWheelAngle = delta; + } else { + m_pendingWheelAngle += delta; + } + + if (abs(m_pendingWheelAngle) >= 600) { + // Sometimes on Linux we're seeing absurdly extreme angles + // on the first wheel event -- discard those entirely + m_pendingWheelAngle = 0; + return 0; + } + + int count = m_pendingWheelAngle / 120; + m_pendingWheelAngle -= count * 120; + return count; + } + +private: + int m_pendingWheelAngle; +}; + +#endif diff -r 57d192e26331 -r 13d9b422f7fe widgets/WidgetScale.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/WidgetScale.h Mon Sep 17 13:51:31 2018 +0100 @@ -0,0 +1,64 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_WIDGET_SCALE_H +#define SV_WIDGET_SCALE_H + +#include +#include + +#include "base/Debug.h" + +class WidgetScale +{ +public: + /** + * Take a "design pixel" size and scale it for the actual + * display. This is relevant to hi-dpi systems that do not do + * pixel doubling (i.e. Windows and Linux rather than OS/X). + */ + static int scalePixelSize(int pixels) { + + static double ratio = 0.0; + if (ratio == 0.0) { + double baseEm; +#ifdef Q_OS_MAC + baseEm = 17.0; +#else + baseEm = 15.0; +#endif + double em = QFontMetrics(QFont()).height(); + ratio = em / baseEm; + SVDEBUG << "WidgetScale::scalePixelSize: baseEm = " << baseEm + << ", platform default font height = " << em + << ", resulting scale factor = " << ratio << endl; + if (ratio < 1.0) { + SVDEBUG << "WidgetScale::scalePixelSize: rounding up to 1.0" + << endl; + ratio = 1.0; + } + } + + int scaled = int(pixels * ratio + 0.5); + if (pixels != 0 && scaled == 0) scaled = 1; + return scaled; + } + + static QSize scaleQSize(QSize size) { + return QSize(scalePixelSize(size.width()), + scalePixelSize(size.height())); + } +}; + +#endif