Mercurial > hg > svgui
changeset 374:64e84e5efb76 spectrogram-cache-rejig
* Merge from trunk
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/Colour3DPlotLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -39,7 +39,8 @@ m_colourScale(LinearScale), m_colourMap(0), m_normalizeColumns(false), - m_normalizeVisibleArea(false) + m_normalizeVisibleArea(false), + m_invertVertical(false) { } @@ -87,6 +88,7 @@ list.push_back("Colour Scale"); list.push_back("Normalize Columns"); list.push_back("Normalize Visible Area"); + list.push_back("Invert Vertical Scale"); return list; } @@ -97,6 +99,16 @@ if (name == "Colour Scale") return tr("Scale"); if (name == "Normalize Columns") return tr("Normalize Columns"); if (name == "Normalize Visible Area") return tr("Normalize Visible Area"); + if (name == "Invert Vertical Scale") return tr("Invert Vertical Scale"); + return ""; +} + +QString +Colour3DPlotLayer::getPropertyIconName(const PropertyName &name) const +{ + if (name == "Normalize Columns") return "normalise-columns"; + if (name == "Normalize Visible Area") return "normalise"; + if (name == "Invert Vertical Scale") return "invert-vertical"; return ""; } @@ -105,6 +117,7 @@ { if (name == "Normalize Columns") return ToggleProperty; if (name == "Normalize Visible Area") return ToggleProperty; + if (name == "Invert Vertical Scale") return ToggleProperty; return ValueProperty; } @@ -113,6 +126,7 @@ { if (name == "Normalize Columns" || name == "Normalize Visible Area" || + name == "Invert Vertical Scale" || name == "Colour Scale") return tr("Scale"); return QString(); } @@ -154,6 +168,11 @@ *deflt = 0; val = (m_normalizeVisibleArea ? 1 : 0); + } else if (name == "Invert Vertical Scale") { + + *deflt = 0; + val = (m_invertVertical ? 1 : 0); + } else { val = Layer::getPropertyRangeAndValue(name, min, max, deflt); } @@ -195,6 +214,8 @@ setNormalizeColumns(value ? true : false); } else if (name == "Normalize Visible Area") { setNormalizeVisibleArea(value ? true : false); + } else if (name == "Invert Vertical Scale") { + setInvertVertical(value ? true : false); } } @@ -246,6 +267,21 @@ return m_normalizeVisibleArea; } +void +Colour3DPlotLayer::setInvertVertical(bool n) +{ + if (m_invertVertical == n) return; + m_invertVertical = n; + cacheInvalid(); + emit layerParametersChanged(); +} + +bool +Colour3DPlotLayer::getInvertVertical() const +{ + return m_invertVertical; +} + bool Colour3DPlotLayer::isLayerScrollable(const View *v) const { @@ -278,6 +314,8 @@ float binHeight = float(v->height()) / m_model->getHeight(); int sy = int((v->height() - y) / binHeight); + if (m_invertVertical) sy = m_model->getHeight() - sy - 1; + float value = m_model->getValueAt(sx0, sy); // std::cerr << "bin value (" << sx0 << "," << sy << ") is " << value << std::endl; @@ -358,12 +396,15 @@ for (size_t i = 0; i < m_model->getHeight(); ++i) { - if ((i % step) != 0) continue; + size_t idx = i; + if (m_invertVertical) idx = m_model->getHeight() - idx - 1; + + if ((idx % step) != 0) continue; int y0 = int(v->height() - (i * binHeight) - 1); - QString text = m_model->getBinName(i); - if (text == "") text = QString("[%1]").arg(i + 1); + QString text = m_model->getBinName(idx); + if (text == "") text = QString("[%1]").arg(idx + 1); paint.drawLine(cw, y0, w, y0); @@ -518,7 +559,12 @@ if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; - m_cache->setPixel(c - firstBin, y, pixel); + if (m_invertVertical) { + m_cache->setPixel(c - firstBin, m_model->getHeight() - y - 1, + pixel); + } else { + m_cache->setPixel(c - firstBin, y, pixel); + } } } } @@ -574,14 +620,21 @@ fillCache(sx0 < 0 ? 0 : sx0, sx1 < 0 ? 0 : sx1); +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + std::cerr << "Colour3DPlotLayer::paint: height = "<< m_model->getHeight() << ", modelStart = " << modelStart << ", resolution = " << modelResolution << ", model rate = " << m_model->getSampleRate() << std::endl; +#endif + if (int(m_model->getHeight()) >= v->height() || - int(modelResolution) < v->getZoomLevel() / 2) { + int(modelResolution * m_model->getSampleRate()) < v->getZoomLevel() / 2) { +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + std::cerr << "calling paintDense" << std::endl; +#endif paintDense(v, paint, rect); return; } #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl; + std::cerr << "Colour3DPlotLayer::paint: w " << x1-x0 << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sx1-sx0 << ", sh " << sh << std::endl; std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", resolution " << m_model->getResolution() << std::endl; #endif @@ -596,7 +649,7 @@ int fx = sx * int(modelResolution); - if (fx + int(modelResolution) < int(modelStart) || + if (fx + int(modelResolution) <= int(modelStart) || fx > int(modelEnd)) continue; int rx0 = v->getXForFrame(int((fx + int(modelStart)) * srRatio)); @@ -687,7 +740,7 @@ for (int x = x0; x < x1; ++x) { - long xf = long(v->getFrameForX(x) / srRatio); + long xf = long(v->getFrameForX(x)); if (xf < 0) { for (int y = 0; y < h; ++y) { img.setPixel(x - x0, y, m_cache->color(0)); @@ -695,6 +748,8 @@ continue; } + xf /= srRatio; + float sx0 = (float(xf) - modelStart) / modelResolution; float sx1 = (float(v->getFrameForX(x+1) / srRatio) - modelStart) / modelResolution;
--- a/layer/Colour3DPlotLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/Colour3DPlotLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -75,6 +75,7 @@ virtual PropertyList getProperties() const; virtual PropertyType getPropertyType(const PropertyName &) const; virtual QString getPropertyLabel(const PropertyName &) const; + virtual QString getPropertyIconName(const PropertyName &) const; virtual QString getPropertyGroupName(const PropertyName &) const; virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const; @@ -97,6 +98,9 @@ void setNormalizeVisibleArea(bool n); bool getNormalizeVisibleArea() const; + void setInvertVertical(bool i); + bool getInvertVertical() const; + virtual const Model *getSliceableModel() const { return m_model; } virtual void toXml(QTextStream &stream, QString indent = "", @@ -116,6 +120,7 @@ int m_colourMap; bool m_normalizeColumns; bool m_normalizeVisibleArea; + bool m_invertVertical; void getColumn(size_t col, DenseThreeDimensionalModel::Column &) const;
--- a/layer/ImageLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/ImageLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -30,6 +30,7 @@ #include <QInputDialog> #include <QMutexLocker> #include <QTextStream> +#include <QMessageBox> #include <iostream> #include <cmath> @@ -802,7 +803,7 @@ } void -ImageLayer::copy(Selection s, Clipboard &to) +ImageLayer::copy(View *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -812,20 +813,39 @@ for (ImageModel::PointList::iterator i = points.begin(); i != points.end(); ++i) { if (s.contains(i->frame)) { - //!!! inadequate Clipboard::Point point(i->frame, i->label); + point.setReferenceFrame(alignToReference(v, i->frame)); to.addPoint(point); } } } bool -ImageLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */) +ImageLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) { if (!m_model) return false; const Clipboard::PointList &points = from.getPoints(); + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v, tr("Re-align pasted items?"), + tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + ImageModel::EditCommand *command = new ImageModel::EditCommand(m_model, tr("Paste")); @@ -833,10 +853,23 @@ i != points.end(); ++i) { if (!i->haveFrame()) continue; + size_t frame = 0; - if (frameOffset > 0 || -frameOffset < i->getFrame()) { - frame = i->getFrame() + frameOffset; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } } + ImageModel::Point newPoint(frame); //!!! inadequate @@ -879,7 +912,7 @@ return; } - FileSource *rf = new FileSource(img, true); + FileSource *rf = new FileSource(img, FileSource::ProgressDialog); if (rf->isOK()) { std::cerr << "ok, adding it (local filename = " << rf->getLocalFilename().toStdString() << ")" << std::endl; m_remoteFiles[img] = rf;
--- a/layer/ImageLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/ImageLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -58,8 +58,8 @@ virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(Selection s, Clipboard &to); - virtual bool paste(const Clipboard &from, int frameOffset, + virtual void copy(View *v, Selection s, Clipboard &to); + virtual bool paste(View *v, const Clipboard &from, int frameOffset, bool interactive); virtual bool editOpen(View *, QMouseEvent *); // on double-click
--- a/layer/Layer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/Layer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -68,10 +68,16 @@ (LayerFactory::getInstance()->getLayerType(this)); } +void +Layer::setPresentationName(QString name) +{ + m_presentationName = name; +} + QString Layer::getLayerPresentationName() const { -// QString layerName = objectName(); + if (m_presentationName != "") return m_presentationName; LayerFactory *factory = LayerFactory::getInstance(); QString layerName = factory->getLayerPresentationName @@ -159,6 +165,106 @@ return true; } +size_t +Layer::alignToReference(View *v, size_t frame) const +{ + const Model *m = getModel(); + std::cerr << "Layer::alignToReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << std::endl; + if (m && m->getAlignmentReference()) { + return m->alignToReference(frame); + } else { + return v->alignToReference(frame); + } +} + +size_t +Layer::alignFromReference(View *v, size_t frame) const +{ + const Model *m = getModel(); + std::cerr << "Layer::alignFromReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << std::endl; + if (m && m->getAlignmentReference()) { + return m->alignFromReference(frame); + } else { + return v->alignFromReference(frame); + } +} + +bool +Layer::clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const +{ + // Notes on pasting to an aligned layer: + // + // Each point may have a reference frame that may differ from the + // point's given frame (in its source model). If it has no + // reference frame, we have to assume the source model was not + // aligned or was the reference model: when cutting or copying + // points from a layer, we must always set their reference frame + // correctly if we are aligned. + // + // When pasting: + // - if point's reference and aligned frames differ: + // - if this layer is aligned: + // - if point's aligned frame matches this layer's aligned version + // of point's reference frame: + // - we can paste at reference frame or our frame + // - else + // - we can paste at reference frame, result of aligning reference + // frame in our model, or literal source frame + // - else + // - we can paste at reference (our) frame, or literal source frame + // - else + // - if this layer is aligned: + // - we can paste at reference (point's only available) frame, + // or result of aligning reference frame in our model + // - else + // - we can only paste at reference frame + // + // Which of these alternatives are useful? + // + // Example: we paste between two tracks that are aligned to the + // same reference, and the points are at 10s and 20s in the source + // track, corresponding to 5s and 10s in the reference but 20s and + // 30s in the target track. + // + // The obvious default is to paste at 20s and 30s; if we aren't + // doing that, would it be better to paste at 5s and 10s or at 10s + // and 20s? We probably don't ever want to do the former, do we? + // We either want to be literal all the way through, or aligned + // all the way through. + + for (Clipboard::PointList::const_iterator i = clip.getPoints().begin(); + i != clip.getPoints().end(); ++i) { + + // In principle, we want to know whether the aligned version + // of the reference frame in our layer is the same as the + // source frame contained in the clipboard point. However, + // because of rounding during alignment, that won't + // necessarily be the case even if the clipboard point came + // from our layer! What we need to check is whether, if we + // aligned the clipboard point's frame back to the reference + // using this layer's alignment, we would obtain the same + // reference frame as that for the clipboard point. + + // What if the clipboard point has no reference frame? Then + // we have to treat it as having its own frame as the + // reference (i.e. having been copied from the reference + // model). + + long sourceFrame = i->getFrame(); + long referenceFrame = sourceFrame; + if (i->haveReferenceFrame()) { + referenceFrame = i->getReferenceFrame(); + } + long myMappedFrame = alignToReference(v, sourceFrame); + +// std::cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->haveReferenceFrame() << "), myMappedFrame = " << myMappedFrame << std::endl; + + if (myMappedFrame != referenceFrame) return true; + } + + return false; +} + bool Layer::MeasureRect::operator<(const MeasureRect &mr) const { @@ -488,6 +594,11 @@ { stream << indent; + if (m_presentationName != "") { + extraAttributes = QString("%1 presentationName=\"%2\"") + .arg(extraAttributes).arg(encodeEntities(m_presentationName)); + } + stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5") .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName (LayerFactory::getInstance()->getLayerType(this)))) @@ -517,6 +628,11 @@ { stream << indent; + if (m_presentationName != "") { + extraAttributes = QString("%1 presentationName=\"%2\"") + .arg(extraAttributes).arg(encodeEntities(m_presentationName)); + } + stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5/>\n") .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName (LayerFactory::getInstance()->getLayerType(this))))
--- a/layer/Layer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/Layer.h Wed Feb 27 11:59:42 2008 +0000 @@ -92,9 +92,12 @@ virtual QString getPropertyContainerIconName() const; virtual QString getPropertyContainerName() const { - return objectName(); + if (m_presentationName != "") return m_presentationName; + else return objectName(); } + virtual void setPresentationName(QString name); + virtual QString getLayerPresentationName() const; virtual QPixmap getLayerPresentationPixmap(QSize) const { return QPixmap(); } @@ -151,7 +154,7 @@ return false; } - // Draw and edit modes: + // Draw, erase, and edit modes: // // Layer needs to get actual mouse events, I guess. Draw mode is // probably the easier. @@ -160,6 +163,10 @@ virtual void drawDrag(View *, QMouseEvent *) { } virtual void drawEnd(View *, QMouseEvent *) { } + virtual void eraseStart(View *, QMouseEvent *) { } + virtual void eraseDrag(View *, QMouseEvent *) { } + virtual void eraseEnd(View *, QMouseEvent *) { } + virtual void editStart(View *, QMouseEvent *) { } virtual void editDrag(View *, QMouseEvent *) { } virtual void editEnd(View *, QMouseEvent *) { } @@ -189,7 +196,7 @@ virtual void resizeSelection(Selection, Selection /* newSize */) { } virtual void deleteSelection(Selection) { } - virtual void copy(Selection, Clipboard & /* to */) { } + virtual void copy(View *, Selection, Clipboard & /* to */) { } /** * Paste from the given clipboard onto the layer at the given @@ -198,7 +205,8 @@ * return false if the user cancelled the paste operation. This * function should return true if a paste actually occurred. */ - virtual bool paste(const Clipboard & /* from */, + virtual bool paste(View *, + const Clipboard & /* from */, int /* frameOffset */, bool /* interactive */) { return false; } @@ -461,6 +469,10 @@ protected: void connectSignals(const Model *); + virtual size_t alignToReference(View *v, size_t frame) const; + virtual size_t alignFromReference(View *v, size_t frame) const; + bool clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const; + struct MeasureRect { mutable QRect pixrect; @@ -536,6 +548,8 @@ void paintMeasurementRect(View *v, QPainter &paint, const MeasureRect &r, bool focus) const; + QString m_presentationName; + private: mutable QMutex m_dormancyMutex; mutable std::map<const void *, bool> m_dormancy;
--- a/layer/LayerFactory.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/LayerFactory.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -28,6 +28,8 @@ #include "SliceLayer.h" #include "SliceableLayer.h" +#include "base/Clipboard.h" + #include "data/model/RangeSummarisableTimeValueModel.h" #include "data/model/DenseTimeValueModel.h" #include "data/model/SparseOneDimensionalModel.h" @@ -350,6 +352,10 @@ dynamic_cast<SpectrogramLayer *>(layer)->setChannel(channel); return; } + if (dynamic_cast<SpectrumLayer *>(layer)) { + dynamic_cast<SpectrumLayer *>(layer)->setChannel(channel); + return; + } } Layer * @@ -418,8 +424,8 @@ std::cerr << "LayerFactory::createLayer: Unknown layer type " << type << std::endl; } else { - std::cerr << "LayerFactory::createLayer: Setting object name " - << getLayerPresentationName(type).toStdString() << " on " << layer << std::endl; +// std::cerr << "LayerFactory::createLayer: Setting object name " +// << getLayerPresentationName(type).toStdString() << " on " << layer << std::endl; layer->setObjectName(getLayerPresentationName(type)); setLayerDefaultProperties(type, layer); } @@ -430,14 +436,14 @@ void LayerFactory::setLayerDefaultProperties(LayerType type, Layer *layer) { - std::cerr << "LayerFactory::setLayerDefaultProperties: type " << type << " (name \"" << getLayerTypeName(type).toStdString() << "\"" << std::endl; +// std::cerr << "LayerFactory::setLayerDefaultProperties: type " << type << " (name \"" << getLayerTypeName(type).toStdString() << "\")" << std::endl; QSettings settings; settings.beginGroup("LayerDefaults"); QString defaults = settings.value(getLayerTypeName(type), "").toString(); if (defaults == "") return; - std::cerr << "defaults=\"" << defaults.toStdString() << "\"" << std::endl; +// std::cerr << "defaults=\"" << defaults.toStdString() << "\"" << std::endl; QString xml = layer->toXmlString(); QDomDocument docOld, docNew; @@ -453,9 +459,9 @@ for (unsigned int i = 0; i < attrNodes.length(); ++i) { QDomAttr attr = attrNodes.item(i).toAttr(); if (attr.isNull()) continue; - std::cerr << "append \"" << attr.name().toStdString() - << "\" -> \"" << attr.value().toStdString() << "\"" - << std::endl; +// std::cerr << "append \"" << attr.name().toStdString() +// << "\" -> \"" << attr.value().toStdString() << "\"" +// << std::endl; attrs.append(attr.name(), "", "", attr.value()); } @@ -465,9 +471,9 @@ QDomAttr attr = attrNodes.item(i).toAttr(); if (attr.isNull()) continue; if (attrs.value(attr.name()) == "") { - std::cerr << "append \"" << attr.name().toStdString() - << "\" -> \"" << attr.value().toStdString() << "\"" - << std::endl; +// std::cerr << "append \"" << attr.name().toStdString() +// << "\" -> \"" << attr.value().toStdString() << "\"" +// << std::endl; attrs.append(attr.name(), "", "", attr.value()); } } @@ -478,3 +484,24 @@ settings.endGroup(); } +LayerFactory::LayerType +LayerFactory::getLayerTypeForClipboardContents(const Clipboard &clip) +{ + const Clipboard::PointList &contents = clip.getPoints(); + + bool haveFrame = false; + bool haveValue = false; + bool haveDuration = false; + + for (Clipboard::PointList::const_iterator i = contents.begin(); + i != contents.end(); ++i) { + if (i->haveFrame()) haveFrame = true; + if (i->haveValue()) haveValue = true; + if (i->haveDuration()) haveDuration = true; + } + + if (haveFrame && haveValue && haveDuration) return Notes; + if (haveFrame && haveValue) return TimeValues; + return TimeInstants; +} +
--- a/layer/LayerFactory.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/LayerFactory.h Wed Feb 27 11:59:42 2008 +0000 @@ -21,6 +21,7 @@ class Layer; class Model; +class Clipboard; class LayerFactory { @@ -76,6 +77,8 @@ QString getLayerTypeName(LayerType); LayerType getLayerTypeForName(QString); + LayerType getLayerTypeForClipboardContents(const Clipboard &); + protected: template <typename LayerClass, typename ModelClass> bool trySetModel(Layer *layerBase, Model *modelBase) {
--- a/layer/NoteLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/NoteLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -33,6 +33,7 @@ #include <QPainterPath> #include <QMouseEvent> #include <QTextStream> +#include <QMessageBox> #include <iostream> #include <cmath> @@ -41,8 +42,8 @@ SingleColourLayer(), m_model(0), m_editing(false), - m_originalPoint(0, 0.0, 0, tr("New Point")), - m_editingPoint(0, 0.0, 0, tr("New Point")), + m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), + m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), m_editingCommand(0), m_verticalScale(AutoAlignScale) { @@ -619,7 +620,7 @@ float value = getValueForY(v, e->y()); - m_editingPoint = NoteModel::Point(frame, value, 0, tr("New Point")); + m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point")); m_originalPoint = m_editingPoint; if (m_editingCommand) m_editingCommand->finish(); @@ -670,6 +671,51 @@ } void +NoteLayer::eraseStart(View *v, QMouseEvent *e) +{ + if (!m_model) return; + + NoteModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + + m_editingPoint = *points.begin(); + + if (m_editingCommand) { + m_editingCommand->finish(); + m_editingCommand = 0; + } + + m_editing = true; +} + +void +NoteLayer::eraseDrag(View *v, QMouseEvent *e) +{ +} + +void +NoteLayer::eraseEnd(View *v, QMouseEvent *e) +{ + if (!m_model || !m_editing) return; + + m_editing = false; + + NoteModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + if (points.begin()->frame != m_editingPoint.frame || + points.begin()->value != m_editingPoint.value) return; + + m_editingCommand = new NoteModel::EditCommand + (m_model, tr("Erase Point")); + + m_editingCommand->deletePoint(m_editingPoint); + + m_editingCommand->finish(); + m_editingCommand = 0; + m_editing = false; +} + +void NoteLayer::editStart(View *v, QMouseEvent *e) { // std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; @@ -871,7 +917,7 @@ } void -NoteLayer::copy(Selection s, Clipboard &to) +NoteLayer::copy(View *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -881,19 +927,39 @@ for (NoteModel::PointList::iterator i = points.begin(); i != points.end(); ++i) { if (s.contains(i->frame)) { - Clipboard::Point point(i->frame, i->value, i->duration, i->label); + Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label); + point.setReferenceFrame(alignToReference(v, i->frame)); to.addPoint(point); } } } bool -NoteLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */) +NoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) { if (!m_model) return false; const Clipboard::PointList &points = from.getPoints(); + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v, tr("Re-align pasted items?"), + tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + NoteModel::EditCommand *command = new NoteModel::EditCommand(m_model, tr("Paste")); @@ -902,15 +968,28 @@ if (!i->haveFrame()) continue; size_t frame = 0; - if (frameOffset > 0 || -frameOffset < i->getFrame()) { - frame = i->getFrame() + frameOffset; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } } + NoteModel::Point newPoint(frame); if (i->haveLabel()) newPoint.label = i->getLabel(); if (i->haveValue()) newPoint.value = i->getValue(); else newPoint.value = (m_model->getValueMinimum() + m_model->getValueMaximum()) / 2; + if (i->haveLevel()) newPoint.level = i->getLevel(); if (i->haveDuration()) newPoint.duration = i->getDuration(); else { size_t nextFrame = frame;
--- a/layer/NoteLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/NoteLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -44,6 +44,10 @@ virtual void drawDrag(View *v, QMouseEvent *); virtual void drawEnd(View *v, QMouseEvent *); + virtual void eraseStart(View *v, QMouseEvent *); + virtual void eraseDrag(View *v, QMouseEvent *); + virtual void eraseEnd(View *v, QMouseEvent *); + virtual void editStart(View *v, QMouseEvent *); virtual void editDrag(View *v, QMouseEvent *); virtual void editEnd(View *v, QMouseEvent *); @@ -54,8 +58,8 @@ virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(Selection s, Clipboard &to); - virtual bool paste(const Clipboard &from, int frameOffset, + virtual void copy(View *v, Selection s, Clipboard &to); + virtual bool paste(View *v, const Clipboard &from, int frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; }
--- a/layer/SingleColourLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SingleColourLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -22,12 +22,15 @@ #include <QTextStream> #include <QApplication> +//#define DEBUG_COLOUR_SELECTION 1 + SingleColourLayer::ColourRefCount SingleColourLayer::m_colourRefCount; SingleColourLayer::SingleColourLayer() : m_colour(0), - m_colourExplicitlySet(false) + m_colourExplicitlySet(false), + m_defaultColourSet(false) { setDefaultColourFor(0); } @@ -125,7 +128,13 @@ void SingleColourLayer::setDefaultColourFor(View *v) { - if (m_colourExplicitlySet) return; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << "SingleColourLayer::setDefaultColourFor: m_colourExplicitlySet = " << m_colourExplicitlySet << ", m_defaultColourSet " << m_defaultColourSet << std::endl; +#endif + + if (m_colourExplicitlySet || m_defaultColourSet) return; + + if (v) m_defaultColourSet = true; // v==0 case doesn't really count bool dark = false; if (v) { @@ -148,9 +157,13 @@ // means we're being called from the constructor, and this is // a virtual function hint = getDefaultColourHint(dark, impose); -// std::cerr << "hint = " << hint << ", impose = " << impose << std::endl; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << "hint = " << hint << ", impose = " << impose << std::endl; +#endif } else { -// std::cerr << "(from ctor)" << std::endl; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << "(from ctor)" << std::endl; +#endif } if (hint >= 0 && impose) { @@ -171,15 +184,21 @@ count = m_colourRefCount[index]; } -// std::cerr << "index = " << index << ", count = " << count; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << "index = " << index << ", count = " << count; +#endif if (bestColour < 0 || count < bestCount) { bestColour = index; bestCount = count; -// std::cerr << " *"; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << " *"; +#endif } -// std::cerr << std::endl; +#ifdef DEBUG_COLOUR_SELECTION + std::cerr << std::endl; +#endif } if (bestColour < 0) m_colour = 0; @@ -289,7 +308,9 @@ if (m_colour != colour) { +#ifdef DEBUG_COLOUR_SELECTION std::cerr << "SingleColourLayer::setProperties: changing colour from " << m_colour << " to " << colour << std::endl; +#endif if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() && m_colourRefCount[m_colour] > 0) {
--- a/layer/SingleColourLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SingleColourLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -70,6 +70,7 @@ int m_colour; bool m_colourExplicitlySet; + bool m_defaultColourSet; }; #endif
--- a/layer/SliceLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SliceLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -569,6 +569,13 @@ return SingleColourLayer::getPropertyLabel(name); } +QString +SliceLayer::getPropertyIconName(const PropertyName &name) const +{ + if (name == "Normalize") return "normalise"; + return ""; +} + Layer::PropertyType SliceLayer::getPropertyType(const PropertyName &name) const {
--- a/layer/SliceLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SliceLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -52,6 +52,7 @@ 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 int getPropertyRangeAndValue(const PropertyName &,
--- a/layer/SpectrogramLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SpectrogramLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -184,6 +184,14 @@ } QString +SpectrogramLayer::getPropertyIconName(const PropertyName &name) const +{ + if (name == "Normalize Columns") return "normalise-columns"; + if (name == "Normalize Visible Area") return "normalise"; + return ""; +} + +QString SpectrogramLayer::getPropertyLabel(const PropertyName &name) const { if (name == "Colour") return tr("Colour");
--- a/layer/SpectrogramLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SpectrogramLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -79,6 +79,7 @@ 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 int getPropertyRangeAndValue(const PropertyName &,
--- a/layer/SpectrumLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SpectrumLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -47,35 +47,64 @@ SpectrumLayer::~SpectrumLayer() { - //!!! delete parent's model -// for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; + Model *m = const_cast<Model *> + (static_cast<const Model *>(m_sliceableModel)); + m->aboutToDelete(); + m_sliceableModel = 0; + delete m; } void SpectrumLayer::setModel(DenseTimeValueModel *model) { + std::cerr << "SpectrumLayer::setModel(" << model << ") from " << m_originModel << std::endl; + if (m_originModel == model) return; + m_originModel = model; if (m_sliceableModel) { - const Model *oldModel = m_sliceableModel; + Model *m = const_cast<Model *> + (static_cast<const Model *>(m_sliceableModel)); + m->aboutToDelete(); setSliceableModel(0); - // surprised I'm allowed to delete a const pointer -- may be a - // source of future compiler rejection? - delete oldModel; + delete m; } -//!!! setupFFT(); + + m_newFFTNeeded = true; + + emit layerParametersChanged(); +} + +void +SpectrumLayer::setChannel(int channel) +{ + std::cerr << "SpectrumLayer::setChannel(" << channel << ") from " << m_channel << std::endl; + + m_channelSet = true; + + if (m_channel == channel) return; + + m_channel = channel; + + m_newFFTNeeded = true; + + emit layerParametersChanged(); } void SpectrumLayer::setupFFT() { - FFTModel *oldFFT = dynamic_cast<FFTModel *> - (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); - - if (oldFFT) { + if (m_sliceableModel) { + Model *m = const_cast<Model *> + (static_cast<const Model *>(m_sliceableModel)); + m->aboutToDelete(); setSliceableModel(0); - delete oldFFT; + delete m; + } + + if (!m_originModel) { + return; } FFTModel *newFFT = new FFTModel(m_originModel, @@ -97,24 +126,8 @@ } newFFT->resume(); -} -void -SpectrumLayer::setChannel(int channel) -{ - m_channelSet = true; - - FFTModel *fft = dynamic_cast<FFTModel *> - (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); - - if (m_channel == channel) { - if (fft) fft->resume(); - return; - } - - m_channel = channel; - - emit layerParametersChanged(); + m_newFFTNeeded = false; } Layer::PropertyList @@ -136,6 +149,13 @@ return SliceLayer::getPropertyLabel(name); } +QString +SpectrumLayer::getPropertyIconName(const PropertyName &name) const +{ + if (name == "Show Peak Frequencies") return "show-peaks"; + return SliceLayer::getPropertyIconName(name); +} + Layer::PropertyType SpectrumLayer::getPropertyType(const PropertyName &name) const { @@ -633,11 +653,14 @@ SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const { if (!m_originModel || !m_originModel->isOK() || - !m_originModel->isReady()) return; + !m_originModel->isReady()) { + std::cerr << "SpectrumLayer::paint: no origin model, or origin model not OK or not ready" << std::endl; + return; + } if (m_newFFTNeeded) { + std::cerr << "SpectrumLayer::paint: new FFT needed, calling setupFFT" << std::endl; const_cast<SpectrumLayer *>(this)->setupFFT(); //ugh - m_newFFTNeeded = false; } FFTModel *fft = dynamic_cast<FFTModel *> @@ -653,6 +676,8 @@ pkh = 10; //!!! } + paint.save(); + if (fft && m_showPeaks) { // draw peak lines @@ -792,6 +817,8 @@ px = x; } // } + + paint.restore(); } void
--- a/layer/SpectrumLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/SpectrumLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -24,6 +24,7 @@ #include "data/model/DenseTimeValueModel.h" #include <QColor> +#include <QMutex> class FFTModel; @@ -52,6 +53,7 @@ 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 int getPropertyRangeAndValue(const PropertyName &, @@ -112,6 +114,8 @@ bool m_showPeaks; mutable bool m_newFFTNeeded; + mutable QMutex m_fftMutex; + void setupFFT(); virtual void getBiasCurve(BiasCurve &) const;
--- a/layer/TextLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TextLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -27,6 +27,7 @@ #include <QMouseEvent> #include <QInputDialog> #include <QTextStream> +#include <QMessageBox> #include <iostream> #include <cmath> @@ -448,6 +449,51 @@ } void +TextLayer::eraseStart(View *v, QMouseEvent *e) +{ + if (!m_model) return; + + TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); + if (points.empty()) return; + + m_editingPoint = *points.begin(); + + if (m_editingCommand) { + m_editingCommand->finish(); + m_editingCommand = 0; + } + + m_editing = true; +} + +void +TextLayer::eraseDrag(View *v, QMouseEvent *e) +{ +} + +void +TextLayer::eraseEnd(View *v, QMouseEvent *e) +{ + if (!m_model || !m_editing) return; + + m_editing = false; + + TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); + if (points.empty()) return; + if (points.begin()->frame != m_editingPoint.frame || + points.begin()->height != m_editingPoint.height) return; + + m_editingCommand = new TextModel::EditCommand + (m_model, tr("Erase Point")); + + m_editingCommand->deletePoint(m_editingPoint); + + m_editingCommand->finish(); + m_editingCommand = 0; + m_editing = false; +} + +void TextLayer::editStart(View *v, QMouseEvent *e) { // std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; @@ -626,7 +672,7 @@ } void -TextLayer::copy(Selection s, Clipboard &to) +TextLayer::copy(View *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -637,18 +683,38 @@ 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); } } } bool -TextLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */) +TextLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) { if (!m_model) return false; const Clipboard::PointList &points = from.getPoints(); + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v, tr("Re-align pasted items?"), + tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + TextModel::EditCommand *command = new TextModel::EditCommand(m_model, tr("Paste")); @@ -667,9 +733,21 @@ if (!i->haveFrame()) continue; size_t frame = 0; - if (frameOffset > 0 || -frameOffset < i->getFrame()) { - frame = i->getFrame() + frameOffset; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } } + TextModel::Point newPoint(frame); if (i->haveValue()) {
--- a/layer/TextLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TextLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -44,6 +44,10 @@ virtual void drawDrag(View *v, QMouseEvent *); virtual void drawEnd(View *v, QMouseEvent *); + virtual void eraseStart(View *v, QMouseEvent *); + virtual void eraseDrag(View *v, QMouseEvent *); + virtual void eraseEnd(View *v, QMouseEvent *); + virtual void editStart(View *v, QMouseEvent *); virtual void editDrag(View *v, QMouseEvent *); virtual void editEnd(View *v, QMouseEvent *); @@ -52,8 +56,8 @@ virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(Selection s, Clipboard &to); - virtual bool paste(const Clipboard &from, int frameOffset, + virtual void copy(View *v, Selection s, Clipboard &to); + virtual bool paste(View *v, const Clipboard &from, int frameOffset, bool interactive); virtual bool editOpen(View *, QMouseEvent *); // on double-click
--- a/layer/TimeInstantLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeInstantLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -25,10 +25,12 @@ #include "data/model/SparseOneDimensionalModel.h" #include "widgets/ItemEditDialog.h" +#include "widgets/ListInputDialog.h" #include <QPainter> #include <QMouseEvent> #include <QTextStream> +#include <QMessageBox> #include <iostream> #include <cmath> @@ -493,6 +495,50 @@ } void +TimeInstantLayer::eraseStart(View *v, QMouseEvent *e) +{ + if (!m_model) return; + + SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + + m_editingPoint = *points.begin(); + + if (m_editingCommand) { + m_editingCommand->finish(); + m_editingCommand = 0; + } + + m_editing = true; +} + +void +TimeInstantLayer::eraseDrag(View *v, QMouseEvent *e) +{ +} + +void +TimeInstantLayer::eraseEnd(View *v, QMouseEvent *e) +{ + if (!m_model || !m_editing) return; + + m_editing = false; + + SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + if (points.begin()->frame != m_editingPoint.frame) return; + + m_editingCommand = new SparseOneDimensionalModel::EditCommand + (m_model, tr("Erase Point")); + + m_editingCommand->deletePoint(m_editingPoint); + + m_editingCommand->finish(); + m_editingCommand = 0; + m_editing = false; +} + +void TimeInstantLayer::editStart(View *v, QMouseEvent *e) { std::cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << std::endl; @@ -667,7 +713,7 @@ } void -TimeInstantLayer::copy(Selection s, Clipboard &to) +TimeInstantLayer::copy(View *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -678,18 +724,38 @@ 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); } } } bool -TimeInstantLayer::paste(const Clipboard &from, int frameOffset, bool) +TimeInstantLayer::paste(View *v, const Clipboard &from, int frameOffset, bool) { if (!m_model) return false; const Clipboard::PointList &points = from.getPoints(); + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v, tr("Re-align pasted instants?"), + tr("The instants you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + SparseOneDimensionalModel::EditCommand *command = new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste")); @@ -697,10 +763,29 @@ i != points.end(); ++i) { if (!i->haveFrame()) continue; + size_t frame = 0; - if (frameOffset > 0 || -frameOffset < i->getFrame()) { - frame = i->getFrame() + frameOffset; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } } + + if (frameOffset > 0) frame += frameOffset; + else if (frameOffset < 0) { + if (frame > -frameOffset) frame += frameOffset; + else frame = 0; + } + SparseOneDimensionalModel::Point newPoint(frame); if (i->haveLabel()) { newPoint.label = i->getLabel();
--- a/layer/TimeInstantLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeInstantLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -45,6 +45,10 @@ virtual void drawDrag(View *v, QMouseEvent *); virtual void drawEnd(View *v, QMouseEvent *); + virtual void eraseStart(View *v, QMouseEvent *); + virtual void eraseDrag(View *v, QMouseEvent *); + virtual void eraseEnd(View *v, QMouseEvent *); + virtual void editStart(View *v, QMouseEvent *); virtual void editDrag(View *v, QMouseEvent *); virtual void editEnd(View *v, QMouseEvent *); @@ -55,8 +59,8 @@ virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(Selection s, Clipboard &to); - virtual bool paste(const Clipboard &from, int frameOffset, + virtual void copy(View *v, Selection s, Clipboard &to); + virtual bool paste(View *v, const Clipboard &from, int frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; } @@ -101,6 +105,8 @@ virtual int getDefaultColourHint(bool dark, bool &impose); + bool clipboardAlignmentDiffers(View *v, const Clipboard &) const; + SparseOneDimensionalModel *m_model; bool m_editing; SparseOneDimensionalModel::Point m_editingPoint;
--- a/layer/TimeRulerLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeRulerLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -15,6 +15,8 @@ #include "TimeRulerLayer.h" +#include "LayerFactory.h" + #include "data/model/Model.h" #include "base/RealTime.h" #include "base/ColourDatabase.h" @@ -25,6 +27,8 @@ #include <iostream> #include <cmath> +//#define DEBUG_TIME_RULER_LAYER 1 + using std::cerr; using std::endl; @@ -190,104 +194,150 @@ void TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const { -// std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y() -// << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl; +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y() + << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl; +#endif if (!m_model || !m_model->isOK()) return; int sampleRate = m_model->getSampleRate(); if (!sampleRate) return; - long startFrame = v->getStartFrame(); - long endFrame = v->getEndFrame(); + long startFrame = v->getFrameForX(rect.x() - 50); - int zoomLevel = v->getZoomLevel(); - - long rectStart = startFrame + (rect.x() - 100) * zoomLevel; - long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel; - -// std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl; - paint.save(); -//!!! paint.setClipRect(v->rect()); - - int minPixelSpacing = 50; +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "start frame = " << startFrame << std::endl; +#endif bool quarter = false; int incms = getMajorTickSpacing(v, quarter); - RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate); - long ms = rt.sec * 1000 + rt.msec(); + int ms = lrint(1000.0 * (double(startFrame) / double(sampleRate))); ms = (ms / incms) * incms - incms; - RealTime incRt = RealTime::fromMilliseconds(incms); - long incFrame = RealTime::realTime2Frame(incRt, sampleRate); - int incX = incFrame / zoomLevel; +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "start ms = " << ms << " at step " << incms << std::endl; +#endif + + // Calculate the number of ticks per increment -- approximate + // values for x and frame counts here will do, no rounding issue. + // We always use the exact incms in our calculations for where to + // draw the actual ticks or lines. + + int minPixelSpacing = 50; + long incFrame = (incms * sampleRate) / 1000; + int incX = incFrame / v->getZoomLevel(); int ticks = 10; if (incX < minPixelSpacing * 2) { ticks = quarter ? 4 : 5; } - QRect oldClipRect = rect; - QRect newClipRect(oldClipRect.x() - 25, oldClipRect.y(), - oldClipRect.width() + 50, oldClipRect.height()); - paint.setClipRect(newClipRect); - paint.setClipRect(rect); + QColor greyColour = getPartialShades(v)[1]; - QColor greyColour = getPartialShades(v)[1]; + paint.save(); while (1) { - rt = RealTime::fromMilliseconds(ms); + // frame is used to determine where to draw the lines, so it + // needs to correspond to an exact pixel (so that we don't get + // a different pixel when scrolling a small amount and + // re-drawing with a different start frame). + + double dms = ms; + long frame = lrint((dms * sampleRate) / 1000.0); + frame /= v->getZoomLevel(); + frame *= v->getZoomLevel(); // so frame corresponds to an exact pixel + ms += incms; - long frame = RealTime::realTime2Frame(rt, sampleRate); - if (frame >= rectEnd) break; + int x = v->getXForFrame(frame); - int x = (frame - startFrame) / zoomLevel; - if (x < rect.x() || x >= rect.x() + rect.width()) continue; +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "Considering frame = " << frame << ", x = " << x << std::endl; +#endif - paint.setPen(greyColour); - paint.drawLine(x, 0, x, v->height()); + if (x >= rect.x() + rect.width() + 50) { +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "X well out of range, ending here" << std::endl; +#endif + break; + } - paint.setPen(getBaseQColor()); - paint.drawLine(x, 0, x, 5); - paint.drawLine(x, v->height() - 6, x, v->height() - 1); + if (x >= rect.x() - 50) { - QString text(QString::fromStdString(rt.toText())); +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "X in range, drawing line here" << std::endl; +#endif - int y; - QFontMetrics metrics = paint.fontMetrics(); - switch (m_labelHeight) { - default: - case LabelTop: - y = 6 + metrics.ascent(); - break; - case LabelMiddle: - y = v->height() / 2 - metrics.height() / 2 + metrics.ascent(); - break; - case LabelBottom: - y = v->height() - metrics.height() + metrics.ascent() - 6; - } + RealTime rt = RealTime::fromMilliseconds(ms); - int tw = metrics.width(text); + QString text(QString::fromStdString(rt.toText())); + QFontMetrics metrics = paint.fontMetrics(); + int tw = metrics.width(text); - if (v->getViewManager() && v->getViewManager()->getOverlayMode() != - ViewManager::NoOverlays) { + if (tw < 50 && + (x < rect.x() - tw/2 || + x >= rect.x() + rect.width() + tw/2)) { +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "hm, maybe X isn't in range after all (x = " << x << ", tw = " << tw << ", rect.x() = " << rect.x() << ", rect.width() = " << rect.width() << ")" << std::endl; +#endif + } - if (v->getLayer(0) == this) { - // backmost layer, don't worry about outlining the text - paint.drawText(x+2 - tw/2, y, text); - } else { - v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText); + paint.setPen(greyColour); + paint.drawLine(x, 0, x, v->height()); + + paint.setPen(getBaseQColor()); + paint.drawLine(x, 0, x, 5); + paint.drawLine(x, v->height() - 6, x, v->height() - 1); + + int y; + switch (m_labelHeight) { + default: + case LabelTop: + y = 6 + metrics.ascent(); + break; + case LabelMiddle: + y = v->height() / 2 - metrics.height() / 2 + metrics.ascent(); + break; + case LabelBottom: + y = v->height() - metrics.height() + metrics.ascent() - 6; + } + + if (v->getViewManager() && v->getViewManager()->getOverlayMode() != + ViewManager::NoOverlays) { + + if (v->getLayer(0) == this) { + // backmost layer, don't worry about outlining the text + paint.drawText(x+2 - tw/2, y, text); + } else { + v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText); + } } } paint.setPen(greyColour); for (int i = 1; i < ticks; ++i) { - rt = rt + (incRt / ticks); - frame = RealTime::realTime2Frame(rt, sampleRate); - x = (frame - startFrame) / zoomLevel; + + dms = ms - incms + (i * double(incms)) / ticks; + frame = lrint((dms * sampleRate) / 1000.0); + frame /= v->getZoomLevel(); + frame *= v->getZoomLevel(); // exact pixel as above + + x = v->getXForFrame(frame); + + if (x < rect.x() || x >= rect.x() + rect.width()) { +#ifdef DEBUG_TIME_RULER_LAYER +// std::cerr << "tick " << i << ": X out of range, going on to next tick" << std::endl; +#endif + continue; + } + +#ifdef DEBUG_TIME_RULER_LAYER + std::cerr << "tick " << i << " in range, drawing at " << x << std::endl; +#endif + int sz = 5; if (ticks == 10) { if ((i % 2) == 1) { @@ -314,6 +364,14 @@ (QString(darkbg ? "White" : "Black")); } +QString TimeRulerLayer::getLayerPresentationName() const +{ + LayerFactory *factory = LayerFactory::getInstance(); + QString layerName = factory->getLayerPresentationName + (factory->getLayerType(this)); + return layerName; +} + void TimeRulerLayer::toXml(QTextStream &stream, QString indent, QString extraAttributes) const
--- a/layer/TimeRulerLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeRulerLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -51,6 +51,8 @@ return false; } + virtual QString getLayerPresentationName() const; + virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const;
--- a/layer/TimeValueLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeValueLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -23,6 +23,7 @@ #include "view/View.h" #include "data/model/SparseTimeValueModel.h" +#include "data/model/Labeller.h" #include "widgets/ItemEditDialog.h" #include "widgets/ListInputDialog.h" @@ -35,6 +36,8 @@ #include <QMouseEvent> #include <QRegExp> #include <QTextStream> +#include <QMessageBox> +#include <QInputDialog> #include <iostream> #include <cmath> @@ -538,6 +541,8 @@ int sampleRate = m_model->getSampleRate(); if (!sampleRate) return; + paint.setRenderHint(QPainter::Antialiasing, false); + // Profiler profiler("TimeValueLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); @@ -599,6 +604,9 @@ if (m_plotStyle != PlotSegmentation) { textY = y - paint.fontMetrics().height() + paint.fontMetrics().ascent(); + if (textY < paint.fontMetrics().ascent() + 1) { + textY = paint.fontMetrics().ascent() + 1; + } } bool haveNext = false; @@ -940,6 +948,51 @@ } void +TimeValueLayer::eraseStart(View *v, QMouseEvent *e) +{ + if (!m_model) return; + + SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + + m_editingPoint = *points.begin(); + + if (m_editingCommand) { + m_editingCommand->finish(); + m_editingCommand = 0; + } + + m_editing = true; +} + +void +TimeValueLayer::eraseDrag(View *v, QMouseEvent *e) +{ +} + +void +TimeValueLayer::eraseEnd(View *v, QMouseEvent *e) +{ + if (!m_model || !m_editing) return; + + m_editing = false; + + SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); + if (points.empty()) return; + if (points.begin()->frame != m_editingPoint.frame || + points.begin()->value != m_editingPoint.value) return; + + m_editingCommand = new SparseTimeValueModel::EditCommand + (m_model, tr("Erase Point")); + + m_editingCommand->deletePoint(m_editingPoint); + + m_editingCommand->finish(); + m_editingCommand = 0; + m_editing = false; +} + +void TimeValueLayer::editStart(View *v, QMouseEvent *e) { // std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; @@ -1136,7 +1189,7 @@ } void -TimeValueLayer::copy(Selection s, Clipboard &to) +TimeValueLayer::copy(View *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -1147,45 +1200,55 @@ 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); } } } bool -TimeValueLayer::paste(const Clipboard &from, int frameOffset, +TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset, bool interactive) { if (!m_model) return false; const Clipboard::PointList &points = from.getPoints(); + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v, tr("Re-align pasted items?"), + tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + SparseTimeValueModel::EditCommand *command = new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); - //!!! Replace all this with a use of Labeller - enum ValueAvailability { UnknownAvailability, NoValues, SomeValues, AllValues }; - enum ValueGeneration { - GenerateNone, - GenerateFromCounter, - GenerateFromFrameNumber, - GenerateFromRealTime, - GenerateFromRealTimeDifference, - GenerateFromTempo, - GenerateFromExistingNeighbour, - GenerateFromLabels - }; - ValueGeneration generation = GenerateNone; + Labeller::ValueType generation = Labeller::ValueNone; bool haveUsableLabels = false; bool haveExistingItems = !(m_model->isEmpty()); + Labeller labeller; + labeller.setSampleRate(m_model->getSampleRate()); if (interactive) { @@ -1232,38 +1295,18 @@ text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?"); } + Labeller::TypeNameMap names = labeller.getTypeNames(); + QStringList options; - std::vector<int> genopts; + std::vector<Labeller::ValueType> genopts; - options << tr("Zero for all items"); - genopts.push_back(int(GenerateNone)); - - options << tr("Whole numbers counting from 1"); - genopts.push_back(int(GenerateFromCounter)); - - options << tr("Item's audio sample frame number"); - genopts.push_back(int(GenerateFromFrameNumber)); - - options << tr("Item's time in seconds"); - genopts.push_back(int(GenerateFromRealTime)); - - options << tr("Duration from the item to the following item"); - genopts.push_back(int(GenerateFromRealTimeDifference)); - - options << tr("Tempo in bpm derived from the duration"); - genopts.push_back(int(GenerateFromTempo)); - - if (haveExistingItems) { - options << tr("Value of the nearest existing item"); - genopts.push_back(int(GenerateFromExistingNeighbour)); + for (Labeller::TypeNameMap::const_iterator i = names.begin(); + i != names.end(); ++i) { + if (i->first == Labeller::ValueNone) options << tr("Zero for all items"); + else options << i->second; + genopts.push_back(i->first); } - if (haveUsableLabels) { - options << tr("Value extracted from the item's label (where possible)"); - genopts.push_back(int(GenerateFromLabels)); - } - - static int prevSelection = 0; bool ok = false; @@ -1273,32 +1316,54 @@ if (!ok) return false; int selection = 0; - generation = GenerateNone; + generation = Labeller::ValueNone; for (QStringList::const_iterator i = options.begin(); i != options.end(); ++i) { if (selected == *i) { - generation = ValueGeneration(genopts[selection]); + generation = genopts[selection]; break; } ++selection; } + + labeller.setType(generation); + + if (generation == Labeller::ValueFromCyclicalCounter || + generation == Labeller::ValueFromTwoLevelCounter) { + int cycleSize = QInputDialog::getInteger + (0, tr("Select cycle size"), + tr("Cycle size:"), 4, 2, 16, 1); + labeller.setCounterCycleSize(cycleSize); + } prevSelection = selection; } } - int counter = 1; - float prevBpm = 120.f; + SparseTimeValueModel::Point prevPoint(0); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { if (!i->haveFrame()) continue; + size_t frame = 0; - if (frameOffset > 0 || -frameOffset < i->getFrame()) { - frame = i->getFrame() + frameOffset; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } } + SparseTimeValueModel::Point newPoint(frame); if (i->haveLabel()) { @@ -1307,81 +1372,33 @@ newPoint.label = QString("%1").arg(i->getValue()); } + bool usePrev = false; + SparseTimeValueModel::Point formerPrevPoint = prevPoint; + if (i->haveValue()) { newPoint.value = i->getValue(); } else { - - switch (generation) { - - case GenerateNone: - newPoint.value = 0; - break; - - case GenerateFromCounter: - newPoint.value = counter; - break; - - case GenerateFromFrameNumber: - newPoint.value = frame; - break; - - case GenerateFromRealTime: - newPoint.value = float(frame) / float(m_model->getSampleRate()); - break; - - case GenerateFromRealTimeDifference: - case GenerateFromTempo: - { - size_t nextFrame = frame; - Clipboard::PointList::const_iterator j = i; - for (; j != points.end(); ++j) { - if (!j->haveFrame()) continue; - if (j != i) break; - } - if (j != points.end()) { - nextFrame = j->getFrame(); - } - if (generation == GenerateFromRealTimeDifference) { - newPoint.value = float(nextFrame - frame) / - float(m_model->getSampleRate()); - } else { - float bpm = prevBpm; - if (nextFrame > frame) { - bpm = (60.f * m_model->getSampleRate()) / - (nextFrame - frame); - } - newPoint.value = bpm; - prevBpm = bpm; - } - break; - } - - case GenerateFromExistingNeighbour: - { - SparseTimeValueModel::PointList points = - m_model->getPoints(frame); - if (points.empty()) points = m_model->getPreviousPoints(frame); - if (points.empty()) points = m_model->getNextPoints(frame); - if (points.empty()) { - newPoint.value = 0.f; - } else { - newPoint.value = points.begin()->value; - } - } - - case GenerateFromLabels: - if (i->haveLabel()) { - // more forgiving than QString::toFloat() - newPoint.value = atof(i->getLabel().toLocal8Bit()); - } else { - newPoint.value = 0.f; - } +// std::cerr << "Setting value on point at " << newPoint.frame << " from labeller"; +// if (i == points.begin()) { +// std::cerr << ", no prev point" << std::endl; +// } else { +// std::cerr << ", prev point is at " << prevPoint.frame << std::endl; +// } + labeller.setValue<SparseTimeValueModel::Point> + (newPoint, (i == points.begin()) ? 0 : &prevPoint); +// std::cerr << "New point value = " << newPoint.value << std::endl; + if (labeller.actingOnPrevPoint() && i != points.begin()) { + usePrev = true; } } - + + if (usePrev) { + command->deletePoint(formerPrevPoint); + command->addPoint(prevPoint); + } + + prevPoint = newPoint; command->addPoint(newPoint); - - ++counter; } command->finish();
--- a/layer/TimeValueLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/TimeValueLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -47,6 +47,10 @@ virtual void drawDrag(View *v, QMouseEvent *); virtual void drawEnd(View *v, QMouseEvent *); + virtual void eraseStart(View *v, QMouseEvent *); + virtual void eraseDrag(View *v, QMouseEvent *); + virtual void eraseEnd(View *v, QMouseEvent *); + virtual void editStart(View *v, QMouseEvent *); virtual void editDrag(View *v, QMouseEvent *); virtual void editEnd(View *v, QMouseEvent *); @@ -57,8 +61,8 @@ virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(Selection s, Clipboard &to); - virtual bool paste(const Clipboard &from, int frameOffset, + virtual void copy(View *v, Selection s, Clipboard &to); + virtual bool paste(View *v, const Clipboard &from, int frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; }
--- a/layer/WaveformLayer.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/WaveformLayer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -108,6 +108,13 @@ return SingleColourLayer::getPropertyLabel(name); } +QString +WaveformLayer::getPropertyIconName(const PropertyName &name) const +{ + if (name == "Normalize Visible Area") return "normalise"; + return ""; +} + Layer::PropertyType WaveformLayer::getPropertyType(const PropertyName &name) const { @@ -389,6 +396,73 @@ static float meterdbs[] = { -40, -30, -20, -15, -10, -5, -3, -2, -1, -0.5, 0 }; +bool +WaveformLayer::getSourceFramesForX(View *v, int x, size_t modelZoomLevel, + size_t &f0, size_t &f1) const +{ + long viewFrame = v->getFrameForX(x); + if (viewFrame < 0) { + f0 = 0; + f1 = 0; + return false; + } + + f0 = viewFrame; + + f0 = f0 / modelZoomLevel; + f0 = f0 * modelZoomLevel; + + viewFrame = v->getFrameForX(x + 1); + + f1 = viewFrame; + f1 = f1 / modelZoomLevel; + f1 = f1 * modelZoomLevel; + + return (f0 < m_model->getEndFrame()); +} + +float +WaveformLayer::getNormalizeGain(View *v, int channel) const +{ + long startFrame = v->getStartFrame(); + long endFrame = v->getEndFrame(); + + // Although a long for purposes of comparison against the view + // start and end frames, these are known to be non-negative + long modelStart = long(m_model->getStartFrame()); + long modelEnd = long(m_model->getEndFrame()); + + size_t rangeStart, rangeEnd; + + if (startFrame < modelStart) rangeStart = modelStart; + else rangeStart = startFrame; + + if (endFrame < 0) rangeEnd = 0; + else if (endFrame > modelEnd) rangeEnd = modelEnd; + else rangeEnd = endFrame; + + if (rangeEnd < rangeStart) rangeEnd = rangeStart; + + RangeSummarisableTimeValueModel::Range range = + m_model->getSummary(channel, rangeStart, rangeEnd - rangeStart); + + size_t minChannel = 0, maxChannel = 0; + bool mergingChannels = false, mixingChannels = false; + + getChannelArrangement(minChannel, maxChannel, + mergingChannels, mixingChannels); + + if (mergingChannels || mixingChannels) { + RangeSummarisableTimeValueModel::Range otherRange = + m_model->getSummary(1, rangeStart, rangeEnd - rangeStart); + range.max = std::max(range.max, otherRange.max); + range.min = std::min(range.min, otherRange.min); + range.absmean = std::min(range.absmean, otherRange.absmean); + } + + return 1.0 / std::max(fabsf(range.max), fabsf(range.min)); +} + void WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const { @@ -396,8 +470,6 @@ return; } - long startFrame = v->getStartFrame(); - long endFrame = v->getEndFrame(); int zoomLevel = v->getZoomLevel(); #ifdef DEBUG_WAVEFORM_PAINT @@ -471,11 +543,26 @@ if (x0 > 0) --x0; if (x1 < v->width()) ++x1; - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1 + 1); + // Our zoom level may differ from that at which the underlying + // model has its blocks. + // Each pixel within our visible range must always draw from + // exactly the same set of underlying audio frames, no matter what + // the range being drawn is. And that set of underlying frames + // must remain the same when we scroll one or more pixels left or + // right. + + size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); + + size_t frame0; + size_t frame1; + size_t spare; + + getSourceFramesForX(v, x0, modelZoomLevel, frame0, spare); + getSourceFramesForX(v, x1, modelZoomLevel, spare, frame1); + #ifdef DEBUG_WAVEFORM_PAINT - std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl; + std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << modelZoomLevel << ")" << std::endl; #endif RangeSummarisableTimeValueModel::RangeBlock *ranges = @@ -500,45 +587,15 @@ m_effectiveGains.push_back(m_gain); } - // Although a long for purposes of comparison against the view - // start and end frames, these are known to be non-negative - long modelStart = long(m_model->getStartFrame()); - long modelEnd = long(m_model->getEndFrame()); - -#ifdef DEBUG_WAVEFORM_PAINT - std::cerr << "Model start = " << modelStart << ", end = " << modelEnd << std::endl; -#endif - for (size_t ch = minChannel; ch <= maxChannel; ++ch) { int prevRangeBottom = -1, prevRangeTop = -1; QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; - size_t rangeStart, rangeEnd; m_effectiveGains[ch] = m_gain; if (m_autoNormalize) { - - if (startFrame < modelStart) rangeStart = modelStart; - else rangeStart = startFrame; - - if (endFrame < 0) rangeEnd = 0; - else if (endFrame > modelEnd) rangeEnd = modelEnd; - else rangeEnd = endFrame; - - if (rangeEnd < rangeStart) rangeEnd = rangeStart; - - RangeSummarisableTimeValueModel::Range range = - m_model->getSummary(ch, rangeStart, rangeEnd - rangeStart); - if (mergingChannels || mixingChannels) { - RangeSummarisableTimeValueModel::Range otherRange = - m_model->getSummary(1, rangeStart, rangeEnd - rangeStart); - range.max = std::max(range.max, otherRange.max); - range.min = std::min(range.min, otherRange.min); - range.absmean = std::min(range.absmean, otherRange.absmean); - } - m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max), - fabsf(range.min)); + m_effectiveGains[ch] = getNormalizeGain(v, ch); } float gain = m_effectiveGains[ch]; @@ -606,25 +663,14 @@ } } } + + m_model->getSummaries(ch, frame0, frame1 - frame0, + *ranges, modelZoomLevel); - if (frame1 < modelStart) continue; +#ifdef DEBUG_WAVEFORM_PAINT + std::cerr << ranges->size() << " ranges from " << frame0 << " to " << frame1 << std::endl; +#endif - size_t modelZoomLevel = zoomLevel; - - if (frame0 < modelStart) rangeStart = modelStart; - else rangeStart = frame0; - - if (frame1 < 0) rangeEnd = 0; - else if (frame1 > modelEnd) rangeEnd = modelEnd; - else rangeEnd = frame1; - - if (rangeEnd < rangeStart) rangeEnd = rangeStart; - - m_model->getSummaries - (ch, rangeStart, rangeEnd - rangeStart, *ranges, modelZoomLevel); - -// std::cerr << ranges->size() << " ranges" << std::endl; - if (mergingChannels || mixingChannels) { if (m_model->getChannelCount() > 1) { if (!otherChannelRanges) { @@ -632,7 +678,7 @@ new RangeSummarisableTimeValueModel::RangeBlock; } m_model->getSummaries - (1, rangeStart, rangeEnd - rangeStart, *otherChannelRanges, + (1, frame0, frame1 - frame0, *otherChannelRanges, modelZoomLevel); } else { if (otherChannelRanges != ranges) delete otherChannelRanges; @@ -643,42 +689,35 @@ for (int x = x0; x <= x1; ++x) { range = RangeSummarisableTimeValueModel::Range(); - size_t index = x - x0; - size_t maxIndex = index; - if (frame0 < modelStart) { - if (index < size_t((modelStart - frame0) / zoomLevel)) { - continue; - } else { - index -= ((modelStart - frame0) / zoomLevel); - maxIndex = index; - } + size_t f0, f1; + if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue; + f1 = f1 - 1; + + if (f0 < frame0) { + std::cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << std::endl; + continue; } - - if (int(modelZoomLevel) != zoomLevel) { - index = size_t((double(index) * zoomLevel) / modelZoomLevel); + size_t i0 = (f0 - frame0) / modelZoomLevel; + size_t i1 = (f1 - frame0) / modelZoomLevel; - if (int(modelZoomLevel) < zoomLevel) { - // Peaks may be missed! The model should avoid - // this by rounding zoom levels up rather than - // down, but we'd better cope in case it doesn't - maxIndex = index; - } else { - maxIndex = size_t((double(index + 1) * zoomLevel) - / modelZoomLevel) - 1; - } - } +#ifdef DEBUG_WAVEFORM_PAINT + std::cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << std::endl; +#endif - if (ranges && index < ranges->size()) { + if (i1 > i0 + 1) { + std::cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << std::endl; + } - range = (*ranges)[index]; + if (ranges && i0 < ranges->size()) { - if (maxIndex > index && maxIndex < ranges->size()) { - range.max = std::max(range.max, (*ranges)[maxIndex].max); - range.min = std::min(range.min, (*ranges)[maxIndex].min); - range.absmean = (range.absmean + - (*ranges)[maxIndex].absmean) / 2; + range = (*ranges)[i0]; + + if (i1 > i0 && i1 < ranges->size()) { + range.max = std::max(range.max, (*ranges)[i1].max); + range.min = std::min(range.min, (*ranges)[i1].min); + range.absmean = (range.absmean + (*ranges)[i1].absmean) / 2; } } else { @@ -689,28 +728,28 @@ if (mergingChannels) { - if (otherChannelRanges && index < otherChannelRanges->size()) { + if (otherChannelRanges && i0 < otherChannelRanges->size()) { range.max = fabsf(range.max); - range.min = -fabsf((*otherChannelRanges)[index].max); + range.min = -fabsf((*otherChannelRanges)[i0].max); range.absmean = (range.absmean + - (*otherChannelRanges)[index].absmean) / 2; + (*otherChannelRanges)[i0].absmean) / 2; - if (maxIndex > index && maxIndex < otherChannelRanges->size()) { + if (i1 > i0 && i1 < otherChannelRanges->size()) { // let's not concern ourselves about the mean range.min = std::min (range.min, - -fabsf((*otherChannelRanges)[maxIndex].max)); + -fabsf((*otherChannelRanges)[i1].max)); } } } else if (mixingChannels) { - if (otherChannelRanges && index < otherChannelRanges->size()) { + if (otherChannelRanges && i0 < otherChannelRanges->size()) { - range.max = (range.max + (*otherChannelRanges)[index].max) / 2; - range.min = (range.min + (*otherChannelRanges)[index].min) / 2; - range.absmean = (range.absmean + (*otherChannelRanges)[index].absmean) / 2; + range.max = (range.max + (*otherChannelRanges)[i0].max) / 2; + range.min = (range.min + (*otherChannelRanges)[i0].min) / 2; + range.absmean = (range.absmean + (*otherChannelRanges)[i0].absmean) / 2; } } @@ -883,11 +922,12 @@ if (!m_model || !m_model->isOK()) return ""; - long f0 = v->getFrameForX(x); - long f1 = v->getFrameForX(x + 1); + int zoomLevel = v->getZoomLevel(); - if (f0 < 0) f0 = 0; - if (f1 <= f0) return ""; + size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); + + size_t f0, f1; + if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return ""; QString text;
--- a/layer/WaveformLayer.h Mon Nov 19 15:50:37 2007 +0000 +++ b/layer/WaveformLayer.h Wed Feb 27 11:59:42 2008 +0000 @@ -53,6 +53,7 @@ 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 int getPropertyRangeAndValue(const PropertyName &, @@ -201,6 +202,11 @@ float getValueForY(const View *v, int y, size_t &channel) const; + bool getSourceFramesForX(View *v, int x, size_t modelZoomLevel, + size_t &f0, size_t &f1) const; + + float getNormalizeGain(View *v, int channel) const; + virtual void flagBaseColourChanged() { m_cacheValid = false; } float m_gain;
--- a/view/Overview.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/view/Overview.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -71,6 +71,7 @@ void Overview::modelReplaced() { + m_playPointerFrame = getAlignedPlaybackFrame(); View::modelReplaced(); } @@ -116,6 +117,8 @@ { bool changed = false; + f = getAlignedPlaybackFrame(); + if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true; m_playPointerFrame = f; @@ -179,6 +182,15 @@ long f0 = w->getFrameForX(0); long f1 = w->getFrameForX(w->width()); + if (f0 >= 0) { + size_t rf0 = w->alignToReference(f0); + f0 = alignFromReference(rf0); + } + if (f1 >= 0) { + size_t rf1 = w->alignToReference(f1); + f1 = alignFromReference(rf1); + } + int x0 = getXForFrame(f0); int x1 = getXForFrame(f1); @@ -200,12 +212,16 @@ Overview::mousePressEvent(QMouseEvent *e) { m_clickPos = e->pos(); + long clickFrame = getFrameForX(m_clickPos.x()); + if (clickFrame > 0) m_dragCentreFrame = clickFrame; + else m_dragCentreFrame = 0; + m_clickedInRange = true; + for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { - if (*i) { - m_clickedInRange = true; - m_dragCentreFrame = ((View *)*i)->getCentreFrame(); - break; - } + if (*i && (*i)->getAligningModel() == getAligningModel()) { + m_dragCentreFrame = (*i)->getCentreFrame(); + break; + } } } @@ -242,7 +258,8 @@ if (std::max(m_centreFrame, newCentreFrame) - std::min(m_centreFrame, newCentreFrame) > size_t(m_zoomLevel)) { - emit centreFrameChanged(newCentreFrame, true, PlaybackScrollContinuous); + size_t rf = alignToReference(newCentreFrame); + emit centreFrameChanged(rf, true, PlaybackScrollContinuous); } } @@ -250,7 +267,9 @@ Overview::mouseDoubleClickEvent(QMouseEvent *e) { long frame = getFrameForX(e->x()); - emit centreFrameChanged(frame, true, PlaybackScrollContinuous); + size_t rf = 0; + if (frame > 0) rf = alignToReference(frame); + emit centreFrameChanged(rf, true, PlaybackScrollContinuous); } void
--- a/view/Pane.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/view/Pane.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -22,6 +22,7 @@ #include "ViewManager.h" #include "base/CommandHistory.h" #include "base/TextAbbrev.h" +#include "base/Preferences.h" #include "layer/WaveformLayer.h" //!!! ugh @@ -49,6 +50,8 @@ #include "widgets/KeyReference.h" //!!! should probably split KeyReference into a data class in base and another that shows the widget +//#define DEBUG_PANE + using std::cerr; using std::endl; @@ -63,6 +66,8 @@ m_ctrlPressed(false), m_navigating(false), m_resizing(false), + m_editing(false), + m_releasing(false), m_centreLineVisible(true), m_scaleWidth(0), m_headsUpDisplay(0), @@ -388,6 +393,7 @@ View::paintEvent(e); paint.begin(this); + setPaintFont(paint); if (e) paint.setClipRect(r); @@ -1209,6 +1215,8 @@ return; } +// std::cerr << "mousePressEvent" << std::endl; + m_clickPos = e->pos(); m_mousePos = m_clickPos; m_clickedInRange = true; @@ -1222,6 +1230,9 @@ if (m_manager) mode = m_manager->getToolMode(); m_navigating = false; + m_resizing = false; + m_editing = false; + m_releasing = false; if (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton) || @@ -1276,9 +1287,10 @@ if (snapFrame < 0) snapFrame = 0; m_selectionStartFrame = snapFrame; if (m_manager) { - m_manager->setInProgressSelection(Selection(snapFrame, - snapFrame + resolution), - !m_ctrlPressed); + m_manager->setInProgressSelection + (Selection(alignToReference(snapFrame), + alignToReference(snapFrame + resolution)), + !m_ctrlPressed); } m_resizing = false; @@ -1293,14 +1305,17 @@ layer->drawStart(this, e); } + } else if (mode == ViewManager::EraseMode) { + + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseStart(this, e); + } + } else if (mode == ViewManager::EditMode) { - if (!editSelectionStart(e)) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->editStart(this, e); - } - } + // Do nothing here -- we'll do it in mouseMoveEvent when the + // drag threshold has been passed } else if (mode == ViewManager::MeasureMode) { @@ -1319,9 +1334,13 @@ return; } +// std::cerr << "mouseReleaseEvent" << std::endl; + ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); + m_releasing = true; + if (m_clickedInRange) { mouseMoveEvent(e); } @@ -1348,7 +1367,10 @@ } else if (mode == ViewManager::SelectMode) { - if (!hasTopLayerTimeXAxis()) return; + if (!hasTopLayerTimeXAxis()) { + m_releasing = false; + return; + } if (m_manager && m_manager->haveInProgressSelection()) { @@ -1378,15 +1400,25 @@ update(); } + } else if (mode == ViewManager::EraseMode) { + + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseEnd(this, e); + update(); + } + } else if (mode == ViewManager::EditMode) { - if (!editSelectionEnd(e)) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->editEnd(this, e); - update(); - } - } + if (m_editing) { + if (!editSelectionEnd(e)) { + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->editEnd(this, e); + update(); + } + } + } } else if (mode == ViewManager::MeasureMode) { @@ -1397,6 +1429,7 @@ } m_clickedInRange = false; + m_releasing = false; emit paneInteractedWith(); } @@ -1408,8 +1441,24 @@ return; } +// std::cerr << "mouseMoveEvent" << std::endl; + updateContextHelp(&e->pos()); + if (m_navigating && m_clickedInRange && !m_releasing) { + + // if no buttons pressed, and not called from + // mouseReleaseEvent, we want to reset clicked-ness (to avoid + // annoying continual drags when we moved the mouse outside + // the window after pressing button first time). + + if (!(e->buttons() & Qt::LeftButton) && + !(e->buttons() & Qt::MidButton)) { + m_clickedInRange = false; + return; + } + } + ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); @@ -1484,14 +1533,53 @@ layer->drawDrag(this, e); } + } else if (mode == ViewManager::EraseMode) { + + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseDrag(this, e); + } + } else if (mode == ViewManager::EditMode) { - if (!editSelectionDrag(e)) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->editDrag(this, e); - } - } + if (m_editing) { + if (!editSelectionDrag(e)) { + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->editDrag(this, e); + } + } + } + + if (!m_editing) { + + DragMode newDragMode = updateDragMode + (m_dragMode, + m_clickPos, + e->pos(), + true, // can move horiz + true, // can move vert + true, // resist horiz + true); // resist vert + + if (newDragMode != UnresolvedDrag) { + + m_editing = true; + + QMouseEvent clickEvent(QEvent::MouseButtonPress, + m_clickPos, + Qt::NoButton, + e->buttons(), + e->modifiers()); + + if (!editSelectionStart(&clickEvent)) { + Layer *layer = getSelectedLayer(); + if (layer && layer->isLayerEditable()) { + layer->editStart(this, &clickEvent); + } + } + } + } } else if (mode == ViewManager::MeasureMode) { @@ -1592,51 +1680,17 @@ // If the top layer is incapable of being dragged // vertically, the logic is short circuited. - int xdiff = e->x() - m_clickPos.x(); - int ydiff = e->y() - m_clickPos.y(); - int smallThreshold = 10, bigThreshold = 50; + m_dragMode = updateDragMode + (m_dragMode, + m_clickPos, + e->pos(), + true, // can move horiz + canTopLayerMoveVertical(), // can move vert + canTopLayerMoveVertical() || (m_manager && m_manager->isPlaying()), // resist horiz + !(m_manager && m_manager->isPlaying())); // resist vert - bool canMoveVertical = canTopLayerMoveVertical(); - bool canMoveHorizontal = true; - - if (!canMoveHorizontal) { - m_dragMode = HorizontalDrag; - } - - if (m_dragMode == UnresolvedDrag) { - - if (abs(ydiff) > smallThreshold && - abs(ydiff) > abs(xdiff) * 2) { - m_dragMode = VerticalDrag; - } else if (abs(xdiff) > smallThreshold && - abs(xdiff) > abs(ydiff) * 2) { - m_dragMode = HorizontalDrag; - } else if (abs(xdiff) > smallThreshold && - abs(ydiff) > smallThreshold) { - m_dragMode = FreeDrag; - } else { - // When playing, we don't want to disturb the play - // position too easily; when not playing, we don't - // want to move up/down too easily - if (m_manager && m_manager->isPlaying()) { - canMoveHorizontal = false; - } else { - canMoveVertical = false; - } - } - } - - if (m_dragMode == VerticalDrag) { - if (abs(xdiff) > bigThreshold) m_dragMode = FreeDrag; - else canMoveHorizontal = false; - } - - if (m_dragMode == HorizontalDrag && canMoveVertical) { - if (abs(ydiff) > bigThreshold) m_dragMode = FreeDrag; - else canMoveVertical = false; - } - - if (canMoveHorizontal) { + if (m_dragMode == HorizontalDrag || + m_dragMode == FreeDrag) { long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x()); @@ -1649,7 +1703,12 @@ } else { newCentreFrame = 0; } - + +#ifdef DEBUG_PANE + std::cerr << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame << + ", models end frame = " << getModelsEndFrame() << std::endl; +#endif + if (newCentreFrame >= getModelsEndFrame()) { newCentreFrame = getModelsEndFrame(); if (newCentreFrame > 0) --newCentreFrame; @@ -1660,7 +1719,8 @@ } } - if (canMoveVertical) { + if (m_dragMode == VerticalDrag || + m_dragMode == FreeDrag) { float vmin = 0.f, vmax = 0.f; float dmin = 0.f, dmax = 0.f; @@ -1669,10 +1729,15 @@ // std::cerr << "ydiff = " << ydiff << std::endl; + int ydiff = e->y() - m_clickPos.y(); float perpix = (dmax - dmin) / height(); float valdiff = ydiff * perpix; // std::cerr << "valdiff = " << valdiff << std::endl; + if (m_dragMode == UnresolvedDrag && ydiff != 0) { + m_dragMode = VerticalDrag; + } + float newmin = m_dragStartMinValue + valdiff; float newmax = m_dragStartMinValue + (dmax - dmin) + valdiff; if (newmin < vmin) { @@ -1692,6 +1757,65 @@ } } +Pane::DragMode +Pane::updateDragMode(DragMode dragMode, + QPoint origin, + QPoint point, + bool canMoveHorizontal, + bool canMoveVertical, + bool resistHorizontal, + bool resistVertical) +{ + int xdiff = point.x() - origin.x(); + int ydiff = point.y() - origin.y(); + + int smallThreshold = 10, bigThreshold = 80; + +// std::cerr << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = " +// << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << std::endl; + + if (dragMode == UnresolvedDrag) { + + if (abs(ydiff) > smallThreshold && + abs(ydiff) > abs(xdiff) * 2 && + canMoveVertical) { +// std::cerr << "Pane::updateDragMode: passed vertical threshold" << std::endl; + dragMode = VerticalDrag; + } else if (abs(xdiff) > smallThreshold && + abs(xdiff) > abs(ydiff) * 2 && + canMoveHorizontal) { +// std::cerr << "Pane::updateDragMode: passed horizontal threshold" << std::endl; + dragMode = HorizontalDrag; + } else if (abs(xdiff) > smallThreshold && + abs(ydiff) > smallThreshold && + canMoveVertical && + canMoveHorizontal) { +// std::cerr << "Pane::updateDragMode: passed both thresholds" << std::endl; + dragMode = FreeDrag; + } + } + + if (dragMode == VerticalDrag && canMoveHorizontal) { + if (abs(xdiff) > bigThreshold) dragMode = FreeDrag; + } + + if (dragMode == HorizontalDrag && canMoveVertical) { + if (abs(ydiff) > bigThreshold) dragMode = FreeDrag; + } + + if (dragMode == UnresolvedDrag) { + if (!resistHorizontal && xdiff != 0) { + dragMode = HorizontalDrag; + } + if (!resistVertical && ydiff != 0) { + if (dragMode == HorizontalDrag) dragMode = FreeDrag; + else dragMode = VerticalDrag; + } + } + + return dragMode; +} + void Pane::dragExtendSelection(QMouseEvent *e) { @@ -1727,7 +1851,8 @@ } if (m_manager) { - m_manager->setInProgressSelection(Selection(min, max), + m_manager->setInProgressSelection(Selection(alignToReference(min), + alignToReference(max)), !m_resizing && !m_ctrlPressed); } @@ -2187,6 +2312,10 @@ case ViewManager::DrawMode: setCursor(Qt::CrossCursor); break; + + case ViewManager::EraseMode: + setCursor(Qt::CrossCursor); + break; case ViewManager::MeasureMode: if (m_measureCursor1) setCursor(*m_measureCursor1); @@ -2326,6 +2455,13 @@ if (editable) { help = tr("Click to add a new item in the active layer"); } + + } else if (mode == ViewManager::EraseMode) { + + //!!! could call through to a layer function to find out exact meaning + if (editable) { + help = tr("Click to erase an item from the active layer"); + } } else if (mode == ViewManager::EditMode) {
--- a/view/Pane.h Mon Nov 19 15:50:37 2007 +0000 +++ b/view/Pane.h Wed Feb 27 11:59:42 2008 +0000 @@ -142,6 +142,8 @@ bool m_navigating; bool m_resizing; + bool m_editing; + bool m_releasing; size_t m_dragCentreFrame; float m_dragStartMinValue; bool m_centreLineVisible; @@ -158,6 +160,14 @@ }; DragMode m_dragMode; + DragMode updateDragMode(DragMode currentMode, + QPoint origin, + QPoint currentPoint, + bool canMoveHorizontal, + bool canMoveVertical, + bool resistHorizontal, + bool resistVertical); + QWidget *m_headsUpDisplay; Panner *m_vpan; Thumbwheel *m_hthumb;
--- a/view/PaneStack.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/view/PaneStack.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -495,14 +495,7 @@ std::cerr << "PaneStack::sizePropertyStacks: max min width " << maxMinWidth << std::endl; #endif -//#ifdef Q_WS_MAC - // This is necessary to compensate for cb->setMinimumSize(10, 10) - // in PropertyBox in the Mac version (to avoid a mysterious crash) - // ... no longer necessary with qt4.2 -// int setWidth = maxMinWidth * 3 / 2; -//#else int setWidth = maxMinWidth; -//#endif m_propertyStackStack->setMaximumWidth(setWidth + 10); @@ -511,6 +504,7 @@ m_panes[i].propertyStack->setMinimumWidth(setWidth); } + emit propertyStacksResized(setWidth); emit propertyStacksResized(); }
--- a/view/PaneStack.h Mon Nov 19 15:50:37 2007 +0000 +++ b/view/PaneStack.h Wed Feb 27 11:59:42 2008 +0000 @@ -74,6 +74,7 @@ void currentPaneChanged(Pane *pane); void currentLayerChanged(Pane *pane, Layer *layer); void rightButtonMenuRequested(Pane *pane, QPoint position); + void propertyStacksResized(int width); void propertyStacksResized(); void contextHelpChanged(const QString &);
--- a/view/View.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/view/View.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -19,6 +19,7 @@ #include "base/ZoomConstraint.h" #include "base/Profiler.h" #include "base/Pitch.h" +#include "base/Preferences.h" #include "layer/TimeRulerLayer.h" #include "layer/SingleColourLayer.h" @@ -31,6 +32,7 @@ #include <QApplication> #include <QProgressDialog> #include <QTextStream> +#include <QFont> #include <iostream> #include <cassert> @@ -326,8 +328,15 @@ changeVisible = true; } - if (e) emit centreFrameChanged(alignFromReference(f), - m_followPan, m_followPlay); + if (e) { + size_t rf = alignToReference(f); +#ifdef DEBUG_VIEW_WIDGET_PAINT + std::cerr << "View[" << this << "]::setCentreFrame(" << f + << "): emitting centreFrameChanged(" + << rf << ")" << std::endl; +#endif + emit centreFrameChanged(rf, m_followPan, m_followPlay); + } } return changeVisible; @@ -344,6 +353,11 @@ { long z = (long)m_zoomLevel; long frame = m_centreFrame - (width()/2) * z; + +#ifdef DEBUG_VIEW_WIDGET_PAINT + std::cerr << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << std::endl; +#endif + frame = (frame / z) * z; // this is start frame return frame + x * z; } @@ -498,9 +512,6 @@ View::LayerProgressBar::LayerProgressBar(QWidget *parent) : QProgressBar(parent) { - QFont f(font()); - f.setPointSize(f.pointSize() * 8 / 10); - setFont(f); } void @@ -518,6 +529,12 @@ m_progressBars[layer]->setMinimum(0); m_progressBars[layer]->setMaximum(100); m_progressBars[layer]->setMinimumWidth(80); + + QFont f(m_progressBars[layer]->font()); + int fs = Preferences::getInstance()->getViewFontSize(); + f.setPointSize(std::min(fs, int(ceil(fs * 0.85)))); + + m_progressBars[layer]->setFont(f); m_progressBars[layer]->hide(); connect(layer, SIGNAL(layerParametersChanged()), @@ -650,7 +667,10 @@ connect(this, SIGNAL(zoomLevelChanged(unsigned long, bool)), m_manager, SLOT(viewZoomLevelChanged(unsigned long, bool))); - if (m_followPlay != PlaybackIgnore) { + if (m_followPlay == PlaybackScrollPage) { +// std::cerr << "View::setViewManager: setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << std::endl; + setCentreFrame(m_manager->getGlobalCentreFrame(), false); + } else if (m_followPlay == PlaybackScrollContinuous) { // std::cerr << "View::setViewManager: setting centre frame to playback frame: " << m_manager->getPlaybackFrame() << std::endl; setCentreFrame(m_manager->getPlaybackFrame(), false); } else if (m_followPan) { @@ -745,6 +765,8 @@ m_cache = 0; } + emit layerModelChanged(); + checkProgress(obj); update(); @@ -869,10 +891,15 @@ } void -View::globalCentreFrameChanged(unsigned long f) +View::globalCentreFrameChanged(unsigned long rf) { if (m_followPan) { - setCentreFrame(alignToReference(f), false); + size_t f = alignFromReference(rf); +#ifdef DEBUG_VIEW_WIDGET_PAINT + std::cerr << "View[" << this << "]::globalCentreFrameChanged(" << rf + << "): setting centre frame to " << f << std::endl; +#endif + setCentreFrame(f, false); } } @@ -1098,6 +1125,7 @@ } Model *anyModel = 0; + Model *alignedModel = 0; Model *goodModel = 0; for (LayerList::const_iterator i = m_layers.begin(); @@ -1111,8 +1139,10 @@ Model *model = (*i)->getModel(); if (!model) continue; + anyModel = model; + if (model->getAlignmentReference()) { - anyModel = model; + alignedModel = model; if (layer->isLayerOpaque() || dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { goodModel = model; @@ -1121,6 +1151,7 @@ } if (goodModel) return goodModel; + else if (alignedModel) return alignedModel; else return anyModel; } @@ -1150,17 +1181,17 @@ Model *aligningModel = getAligningModel(); if (!aligningModel) return pf; - +/* Model *pm = m_manager->getPlaybackModel(); // std::cerr << "View[" << this << "]::getAlignedPlaybackFrame: pf = " << pf; if (pm) { - pf = pm->alignFromReference(pf); + pf = pm->alignToReference(pf); // std::cerr << " -> " << pf; } - - int af = aligningModel->alignToReference(pf); +*/ + int af = aligningModel->alignFromReference(pf); // std::cerr << ", aligned = " << af << std::endl; return af; @@ -1389,6 +1420,14 @@ } void +View::setPaintFont(QPainter &paint) +{ + QFont font(paint.font()); + font.setPointSize(Preferences::getInstance()->getViewFontSize()); + paint.setFont(font); +} + +void View::paintEvent(QPaintEvent *e) { // Profiler prof("View::paintEvent", false); @@ -1573,7 +1612,7 @@ if (repaintCache) paint.begin(m_cache); else paint.begin(this); - + setPaintFont(paint); paint.setClipRect(cacheRect); paint.setPen(getBackground()); @@ -1614,7 +1653,7 @@ paint.begin(this); paint.setClipRect(nonCacheRect); - + setPaintFont(paint); if (scrollables.empty()) { paint.setPen(getBackground()); paint.setBrush(getBackground()); @@ -1632,6 +1671,7 @@ paint.end(); paint.begin(this); + setPaintFont(paint); if (e) paint.setClipRect(e->rect()); if (!m_selectionCached) { drawSelections(paint); @@ -1714,8 +1754,8 @@ for (MultiSelection::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { - int p0 = getXForFrame(i->getStartFrame()); - int p1 = getXForFrame(i->getEndFrame()); + int p0 = getXForFrame(alignFromReference(i->getStartFrame())); + int p1 = getXForFrame(alignFromReference(i->getEndFrame())); if (p1 < 0 || p0 > width()) continue; @@ -1875,6 +1915,8 @@ int labelCount = 0; + // top-left point, x-coord + if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) { axs = QString("%1 %2").arg(v0).arg(u0); if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { @@ -1884,6 +1926,8 @@ aw = paint.fontMetrics().width(axs); ++labelCount; } + + // bottom-right point, x-coord if (r.width() > 0) { if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) { @@ -1895,15 +1939,19 @@ bw = paint.fontMetrics().width(bxs); } } + + // dimension, width if (b0 && b1 && v1 != v0 && u0 == u1) { - dxs = QString("(%1 %2)").arg(fabs(v1 - v0)).arg(u1); + dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1); dw = paint.fontMetrics().width(dxs); } b0 = false; b1 = false; + // top-left point, y-coord + if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) { ays = QString("%1 %2").arg(v0).arg(u0); if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { @@ -1914,6 +1962,8 @@ ++labelCount; } + // bottom-right point, y-coord + if (r.height() > 0) { if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) { bys = QString("%1 %2").arg(v1).arg(u1); @@ -1929,13 +1979,24 @@ float dy = 0.f; QString du; + // dimension, height + if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(), dy, du)) && dy != 0) { if (du != "") { - dys = QString("(%1 %2)").arg(dy).arg(du); + if (du == "Hz") { + int semis; + float cents; + semis = Pitch::getPitchForFrequencyDifference(v0, v1, ¢s); + dys = QString("[%1 %2 (%3)]") + .arg(dy).arg(du) + .arg(Pitch::getLabelForPitchRange(semis, cents)); + } else { + dys = QString("[%1 %2]").arg(dy).arg(du); + } } else { - dys = QString("(%1)").arg(dy); + dys = QString("[%1]").arg(dy); } dw = std::max(dw, paint.fontMetrics().width(dys)); }
--- a/view/View.h Mon Nov 19 15:50:37 2007 +0000 +++ b/view/View.h Wed Feb 27 11:59:42 2008 +0000 @@ -271,6 +271,8 @@ void propertyContainerSelected(PropertyContainer *pc); void propertyChanged(PropertyContainer::PropertyName); + void layerModelChanged(); + void centreFrameChanged(unsigned long frame, bool globalScroll, PlaybackFollowMode followMode); @@ -308,7 +310,8 @@ virtual void drawSelections(QPainter &); virtual bool shouldLabelSelections() const { return true; } virtual bool render(QPainter &paint, int x0, size_t f0, size_t f1); - + virtual void setPaintFont(QPainter &paint); + typedef std::vector<Layer *> LayerList; int getModelsSampleRate() const;
--- a/view/ViewManager.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/view/ViewManager.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -57,7 +57,7 @@ settings.endGroup(); if (getGlobalDarkBackground()) { - +/* std::cerr << "dark palette:" << std::endl; std::cerr << "window = " << QApplication::palette().color(QPalette::Window).name().toStdString() << std::endl; std::cerr << "windowtext = " << QApplication::palette().color(QPalette::WindowText).name().toStdString() << std::endl; @@ -70,7 +70,7 @@ std::cerr << "light = " << QApplication::palette().color(QPalette::Light).name().toStdString() << std::endl; std::cerr << "dark = " << QApplication::palette().color(QPalette::Dark).name().toStdString() << std::endl; std::cerr << "mid = " << QApplication::palette().color(QPalette::Mid).name().toStdString() << std::endl; - +*/ m_lightPalette = QPalette(QColor("#000000"), // WindowText QColor("#dddfe4"), // Button QColor("#ffffff"), // Light @@ -83,6 +83,7 @@ } else { +/* std::cerr << "light palette:" << std::endl; std::cerr << "window = " << QApplication::palette().color(QPalette::Window).name().toStdString() << std::endl; std::cerr << "windowtext = " << QApplication::palette().color(QPalette::WindowText).name().toStdString() << std::endl; @@ -95,7 +96,7 @@ std::cerr << "light = " << QApplication::palette().color(QPalette::Light).name().toStdString() << std::endl; std::cerr << "dark = " << QApplication::palette().color(QPalette::Dark).name().toStdString() << std::endl; std::cerr << "mid = " << QApplication::palette().color(QPalette::Mid).name().toStdString() << std::endl; - +*/ m_darkPalette = QPalette(QColor("#ffffff"), // WindowText QColor("#3e3e3e"), // Button QColor("#808080"), // Light @@ -173,6 +174,20 @@ m_playbackModel = model; } +size_t +ViewManager::alignPlaybackFrameToReference(size_t frame) const +{ + if (!m_playbackModel) return frame; + else return m_playbackModel->alignToReference(frame); +} + +size_t +ViewManager::alignReferenceToPlaybackFrame(size_t frame) const +{ + if (!m_playbackModel) return frame; + else return m_playbackModel->alignFromReference(frame); +} + bool ViewManager::haveInProgressSelection() const { @@ -260,18 +275,19 @@ MultiSelection::SelectionList sl = getSelections(); if (sl.empty()) return frame; - size_t selectionStartFrame = sl.begin()->getStartFrame(); - if (frame < selectionStartFrame) { - frame = selectionStartFrame; - return frame; + for (MultiSelection::SelectionList::const_iterator i = sl.begin(); + i != sl.end(); ++i) { + + if (frame < i->getEndFrame()) { + if (frame < i->getStartFrame()) { + return i->getStartFrame(); + } else { + return frame; + } + } } - MultiSelection::SelectionList::iterator i = sl.end(); - --i; - size_t selectionEndFrame = i->getEndFrame(); - if (frame > selectionEndFrame) frame = selectionEndFrame; - - return frame; + return sl.begin()->getStartFrame(); } void
--- a/view/ViewManager.h Mon Nov 19 15:50:37 2007 +0000 +++ b/view/ViewManager.h Wed Feb 27 11:59:42 2008 +0000 @@ -69,6 +69,9 @@ Model *getPlaybackModel() const; void setPlaybackModel(Model *); + size_t alignPlaybackFrameToReference(size_t) const; + size_t alignReferenceToPlaybackFrame(size_t) const; + bool haveInProgressSelection() const; const Selection &getInProgressSelection(bool &exclusive) const; void setInProgressSelection(const Selection &selection, bool exclusive); @@ -98,6 +101,7 @@ SelectMode, EditMode, DrawMode, + EraseMode, MeasureMode }; ToolMode getToolMode() const { return m_toolMode; }
--- a/widgets/AudioDial.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/AudioDial.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -75,6 +75,7 @@ m_knobColor(Qt::black), m_meterColor(Qt::white), m_defaultValue(0), + m_defaultMappedValue(0), m_mappedValue(0), m_noMappedUpdate(false), m_showTooltip(true), @@ -334,15 +335,24 @@ void AudioDial::setDefaultValue(int defaultValue) { m_defaultValue = defaultValue; + if (m_rangeMapper) { + m_defaultMappedValue = m_rangeMapper->getValueForPosition(defaultValue); + } } - void AudioDial::setValue(int value) { QDial::setValue(value); updateMappedValue(value); } +void AudioDial::setDefaultMappedValue(float value) +{ + m_defaultMappedValue = value; + if (m_rangeMapper) { + m_defaultValue = m_rangeMapper->getPositionForValue(value); + } +} void AudioDial::setMappedValue(float mappedValue) { @@ -407,6 +417,18 @@ } } +void +AudioDial::setToDefault() +{ + if (m_rangeMapper) { + setMappedValue(m_defaultMappedValue); + return; + } + int dv = m_defaultValue; + if (dv < minimum()) dv = minimum(); + if (dv > maximum()) dv = maximum(); + setValue(m_defaultValue); +} // Alternate mouse behavior event handlers. void AudioDial::mousePressEvent(QMouseEvent *mouseEvent) @@ -416,10 +438,7 @@ } else if (mouseEvent->button() == Qt::MidButton || ((mouseEvent->button() == Qt::LeftButton) && (mouseEvent->modifiers() & Qt::ControlModifier))) { - int dv = m_defaultValue; - if (dv < minimum()) dv = minimum(); - if (dv > maximum()) dv = maximum(); - setValue(m_defaultValue); + setToDefault(); } else if (mouseEvent->button() == Qt::LeftButton) { m_mousePressed = true; m_posMouse = mouseEvent->pos();
--- a/widgets/AudioDial.h Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/AudioDial.h Wed Feb 27 11:59:42 2008 +0000 @@ -107,8 +107,12 @@ void setValue(int value); + void setDefaultMappedValue(float mappedValue); + void setMappedValue(float mappedValue); + void setToDefault(); + protected: void drawTick(QPainter &paint, float angle, int size, bool internal); virtual void paintEvent(QPaintEvent *); @@ -129,6 +133,7 @@ QColor m_meterColor; int m_defaultValue; + float m_defaultMappedValue; float m_mappedValue; bool m_noMappedUpdate;
--- a/widgets/ImageDialog.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/ImageDialog.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -186,7 +186,7 @@ tr("The URL scheme \"%1\" is not supported") .arg(url.scheme())); } else { - m_remoteFile = new FileSource(url, true); + m_remoteFile = new FileSource(url, FileSource::ProgressDialog); m_remoteFile->waitForData(); if (!m_remoteFile->isOK()) { QMessageBox::critical(this, tr("File download failed"),
--- a/widgets/LabelCounterInputDialog.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/LabelCounterInputDialog.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -37,7 +37,7 @@ layout->addWidget(label, 0, 0); QSpinBox *counter = new QSpinBox; - counter->setMinimum(1); + counter->setMinimum(-10); counter->setMaximum(10000); counter->setSingleStep(1); m_origSecondCounter = m_labeller->getSecondLevelCounterValue(); @@ -47,7 +47,7 @@ layout->addWidget(counter, 0, 1); counter = new QSpinBox; - counter->setMinimum(1); + counter->setMinimum(-10); counter->setMaximum(10000); counter->setSingleStep(1); m_origCounter = m_labeller->getCounterValue();
--- a/widgets/LayerTree.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/LayerTree.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -21,17 +21,34 @@ #include "view/Pane.h" #include "layer/Layer.h" #include "data/model/Model.h" +#include "data/model/WaveFileModel.h" #include <QIcon> #include <iostream> -LayerTreeModel::LayerTreeModel(PaneStack *stack, QObject *parent) : +ModelDataModel::ModelDataModel(PaneStack *stack, bool waveModelsOnly, + QObject *parent) : QAbstractItemModel(parent), - m_stack(stack) + m_stack(stack), + m_waveModelsOnly(waveModelsOnly) { - connect(stack, SIGNAL(paneAdded()), this, SIGNAL(layoutChanged())); - connect(stack, SIGNAL(paneDeleted()), this, SIGNAL(layoutChanged())); + if (m_waveModelsOnly) { + m_modelTypeColumn = -1; + m_modelNameColumn = 0; + m_modelMakerColumn = 1; + m_modelSourceColumn = 2; + m_columnCount = 3; + } else { + m_modelTypeColumn = 0; + m_modelNameColumn = 1; + m_modelMakerColumn = 2; + m_modelSourceColumn = 3; + m_columnCount = 4; + } + + connect(stack, SIGNAL(paneAdded()), this, SLOT(paneAdded())); + connect(stack, SIGNAL(paneDeleted()), this, SLOT(paneDeleted())); for (int i = 0; i < stack->getPaneCount(); ++i) { Pane *pane = stack->getPane(i); @@ -46,6 +63,238 @@ this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); connect(pane, SIGNAL(propertyContainerNameChanged(PropertyContainer *)), this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); + connect(pane, SIGNAL(layerModelChanged()), + this, SLOT(paneLayerModelChanged())); + } + + rebuildModelSet(); +} + +ModelDataModel::~ModelDataModel() +{ +} + +void +ModelDataModel::rebuildModelSet() +{ + std::set<Model *> unfound = m_models; + + for (int i = 0; i < m_stack->getPaneCount(); ++i) { + + Pane *pane = m_stack->getPane(i); + if (!pane) continue; + + for (int j = 0; j < pane->getLayerCount(); ++j) { + + Layer *layer = pane->getLayer(j); + if (!layer) continue; + + Model *model = layer->getModel(); + if (!model) continue; + + if (m_waveModelsOnly) { + if (!dynamic_cast<WaveFileModel *>(model)) continue; + } + + if (m_models.find(model) == m_models.end()) { + connect(model, SIGNAL(aboutToBeDeleted()), + this, SLOT(rebuildModelSet())); + m_models.insert(model); + } else { + unfound.erase(model); + } + } + } + + for (std::set<Model *>::iterator i = unfound.begin(); + i != unfound.end(); ++i) { + m_models.erase(*i); + } + + std::cerr << "ModelDataModel::rebuildModelSet: " << m_models.size() << " models" << std::endl; +} + +void +ModelDataModel::paneAdded() +{ + rebuildModelSet(); + emit layoutChanged(); +} + +void +ModelDataModel::paneDeleted() +{ + rebuildModelSet(); + emit layoutChanged(); +} + +void +ModelDataModel::paneLayerModelChanged() +{ + rebuildModelSet(); + emit layoutChanged(); +} + +void +ModelDataModel::propertyContainerAdded(PropertyContainer *) +{ + rebuildModelSet(); + emit layoutChanged(); +} + +void +ModelDataModel::propertyContainerRemoved(PropertyContainer *) +{ + rebuildModelSet(); + emit layoutChanged(); +} + +void +ModelDataModel::propertyContainerSelected(PropertyContainer *) +{ +} + +void +ModelDataModel::propertyContainerPropertyChanged(PropertyContainer *pc) +{ +} + +void +ModelDataModel::playParametersAudibilityChanged(bool a) +{ +} + +QVariant +ModelDataModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) return QVariant(); + + QObject *obj = static_cast<QObject *>(index.internalPointer()); + int row = index.row(), col = index.column(); + + //!!! not exactly the ideal use of a std::set + std::set<Model *>::iterator itr = m_models.begin(); + for (int i = 0; i < row && itr != m_models.end(); ++i, ++itr); + if (itr == m_models.end()) return QVariant(); + + Model *model = *itr; + + if (role != Qt::DisplayRole) { + if (m_waveModelsOnly && col == m_modelNameColumn && + role == Qt::DecorationRole) { + // There is no meaningful icon for a model, in general -- + // the icons we have represent layer types and it would be + // misleading to use them for models. However, if we're + // only showing wave models, we use the waveform icon just + // for decorative purposes. + return QVariant(QIcon(QString(":/icons/waveform.png"))); + } + return QVariant(); + } + + if (col == m_modelTypeColumn) { + return QVariant(model->getTypeName()); + } else if (col == m_modelNameColumn) { + return QVariant(model->objectName()); + } else if (col == m_modelMakerColumn) { + return QVariant(model->getMaker()); + } else if (col == m_modelSourceColumn) { + return QVariant(model->getLocation()); + } + + return QVariant(); +} + +bool +ModelDataModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return false; +} + +Qt::ItemFlags +ModelDataModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = Qt::ItemIsEnabled; + return flags; +} + +QVariant +ModelDataModel::headerData(int section, + 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")); + } + + return QVariant(); +} + +QModelIndex +ModelDataModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) { + if (row >= m_models.size()) return QModelIndex(); + return createIndex(row, column, 0); + } + + return QModelIndex(); +} + +QModelIndex +ModelDataModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +int +ModelDataModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) return m_models.size(); + return 0; +} + +int +ModelDataModel::columnCount(const QModelIndex &parent) const +{ + return m_columnCount; +} + + + +LayerTreeModel::LayerTreeModel(PaneStack *stack, QObject *parent) : + QAbstractItemModel(parent), + m_stack(stack) +{ + m_layerNameColumn = 0; + m_layerVisibleColumn = 1; + m_layerPlayedColumn = 2; + m_modelNameColumn = 3; + m_columnCount = 4; + + connect(stack, SIGNAL(paneAdded()), this, SLOT(paneAdded())); + connect(stack, SIGNAL(paneAboutToBeDeleted(Pane *)), + this, SLOT(paneAboutToBeDeleted(Pane *))); + + for (int i = 0; i < stack->getPaneCount(); ++i) { + Pane *pane = stack->getPane(i); + if (!pane) continue; + connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)), + this, SLOT(propertyContainerAdded(PropertyContainer *))); + connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)), + this, SLOT(propertyContainerRemoved(PropertyContainer *))); + connect(pane, SIGNAL(propertyContainerSelected(PropertyContainer *)), + this, SLOT(propertyContainerSelected(PropertyContainer *))); + connect(pane, SIGNAL(propertyContainerPropertyChanged(PropertyContainer *)), + this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); + connect(pane, SIGNAL(propertyContainerNameChanged(PropertyContainer *)), + this, SLOT(propertyContainerPropertyChanged(PropertyContainer *))); + connect(pane, SIGNAL(layerModelChanged()), + this, SLOT(paneLayerModelChanged())); + for (int j = 0; j < pane->getLayerCount(); ++j) { Layer *layer = pane->getLayer(j); if (!layer) continue; @@ -62,6 +311,20 @@ } void +LayerTreeModel::paneAdded() +{ + emit layoutChanged(); +} + +void +LayerTreeModel::paneAboutToBeDeleted(Pane *pane) +{ + std::cerr << "paneDeleted: " << pane << std::endl; + m_deletedPanes.insert(pane); + emit layoutChanged(); +} + +void LayerTreeModel::propertyContainerAdded(PropertyContainer *) { emit layoutChanged(); @@ -80,6 +343,12 @@ } void +LayerTreeModel::paneLayerModelChanged() +{ + emit layoutChanged(); +} + +void LayerTreeModel::propertyContainerPropertyChanged(PropertyContainer *pc) { for (int i = 0; i < m_stack->getPaneCount(); ++i) { @@ -88,9 +357,9 @@ for (int j = 0; j < pane->getLayerCount(); ++j) { if (pane->getLayer(j) == pc) { emit dataChanged(createIndex(pane->getLayerCount() - j - 1, - 0, pane), + m_layerNameColumn, pane), createIndex(pane->getLayerCount() - j - 1, - 3, pane)); + m_modelNameColumn, pane)); } } } @@ -116,9 +385,9 @@ << params << "," << a << "): row " << pane->getLayerCount() - j - 1 << ", col " << 2 << std::endl; emit dataChanged(createIndex(pane->getLayerCount() - j - 1, - 2, pane), + m_layerPlayedColumn, pane), createIndex(pane->getLayerCount() - j - 1, - 2, pane)); + m_layerPlayedColumn, pane)); } } } @@ -148,7 +417,7 @@ if (pane && pane->getLayerCount() > row) { Layer *layer = pane->getLayer(pane->getLayerCount() - row - 1); if (layer) { - if (col == 0) { + if (col == m_layerNameColumn) { switch (role) { case Qt::DisplayRole: return QVariant(layer->objectName()); @@ -158,14 +427,14 @@ .arg(layer->getPropertyContainerIconName()))); default: break; } - } else if (col == 1) { + } else if (col == m_layerVisibleColumn) { if (role == Qt::CheckStateRole) { return QVariant(layer->isLayerDormant(pane) ? Qt::Unchecked : Qt::Checked); } else if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignHCenter); } - } else if (col == 2) { + } else if (col == m_layerPlayedColumn) { if (role == Qt::CheckStateRole) { PlayParameters *params = layer->getPlayParameters(); if (params) return QVariant(params->isPlayMuted() ? @@ -174,7 +443,7 @@ } else if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignHCenter); } - } else if (col == 3) { + } else if (col == m_modelNameColumn) { Model *model = layer->getModel(); if (model && role == Qt::DisplayRole) { return QVariant(model->objectName()); @@ -200,13 +469,13 @@ Layer *layer = pane->getLayer(pane->getLayerCount() - row - 1); if (!layer) return false; - if (col == 1) { + if (col == m_layerVisibleColumn) { if (role == Qt::CheckStateRole) { layer->showLayer(pane, value.toInt() == Qt::Checked); emit dataChanged(index, index); return true; } - } else if (col == 2) { + } else if (col == m_layerPlayedColumn) { if (role == Qt::CheckStateRole) { PlayParameters *params = layer->getPlayParameters(); if (params) { @@ -226,7 +495,8 @@ Qt::ItemFlags flags = Qt::ItemIsEnabled; if (!index.isValid()) return flags; - if (index.column() == 1 || index.column() == 2) { + if (index.column() == m_layerVisibleColumn || + index.column() == m_layerPlayedColumn) { flags |= Qt::ItemIsUserCheckable; } else if (index.column() == 0) { flags |= Qt::ItemIsSelectable; @@ -241,10 +511,10 @@ int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - if (section == 0) return QVariant(tr("Layer")); - else if (section == 1) return QVariant(tr("Shown")); - else if (section == 2) return QVariant(tr("Played")); - else if (section == 3) return QVariant(tr("Model")); + 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")); } return QVariant(); @@ -280,6 +550,11 @@ { QObject *obj = static_cast<QObject *>(index.internalPointer()); + if (m_deletedPanes.find(obj) != m_deletedPanes.end()) { +// m_deletedPanes.erase(obj); + return QModelIndex(); + } + Pane *pane = dynamic_cast<Pane *>(obj); if (pane) { int index = m_stack->getPaneIndex(pane); @@ -308,10 +583,10 @@ int LayerTreeModel::columnCount(const QModelIndex &parent) const { - if (!parent.isValid()) return 4; + if (!parent.isValid()) return m_columnCount; QObject *obj = static_cast<QObject *>(parent.internalPointer()); - if (obj == m_stack) return 4; // row for a layer + if (obj == m_stack) return m_columnCount; // row for a layer return 1; }
--- a/widgets/LayerTree.h Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/LayerTree.h Wed Feb 27 11:59:42 2008 +0000 @@ -19,10 +19,62 @@ #include <QAbstractItemModel> +#include <set> + class PaneStack; class View; +class Pane; class Layer; class PropertyContainer; +class Model; + +class ModelDataModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + ModelDataModel(PaneStack *stack, bool waveModelsOnly, QObject *parent = 0); + virtual ~ModelDataModel(); + + QVariant data(const QModelIndex &index, int role) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role); + + Qt::ItemFlags flags(const QModelIndex &index) const; + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + + QModelIndex parent(const QModelIndex &index) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + +protected slots: + void paneAdded(); + void paneDeleted(); + void propertyContainerAdded(PropertyContainer *); + void propertyContainerRemoved(PropertyContainer *); + void propertyContainerSelected(PropertyContainer *); + void propertyContainerPropertyChanged(PropertyContainer *); + void playParametersAudibilityChanged(bool); + void paneLayerModelChanged(); + void rebuildModelSet(); + +protected: + PaneStack *m_stack; + bool m_waveModelsOnly; + int m_modelTypeColumn; + int m_modelNameColumn; + int m_modelMakerColumn; + int m_modelSourceColumn; + int m_columnCount; + + std::set<Model *> m_models; +}; class LayerTreeModel : public QAbstractItemModel { @@ -49,15 +101,24 @@ int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; -protected: - PaneStack *m_stack; - protected slots: + void paneAdded(); + void paneAboutToBeDeleted(Pane *); void propertyContainerAdded(PropertyContainer *); void propertyContainerRemoved(PropertyContainer *); void propertyContainerSelected(PropertyContainer *); void propertyContainerPropertyChanged(PropertyContainer *); + void paneLayerModelChanged(); void playParametersAudibilityChanged(bool); + +protected: + PaneStack *m_stack; + std::set<QObject *> m_deletedPanes; + int m_layerNameColumn; + int m_layerVisibleColumn; + int m_layerPlayedColumn; + int m_modelNameColumn; + int m_columnCount; }; #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LayerTreeDialog.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -0,0 +1,103 @@ +/* -*- 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 "LayerTreeDialog.h" + +#include "LayerTree.h" +#include "view/PaneStack.h" + +#include <QTreeView> +#include <QTableView> +#include <QGridLayout> +#include <QGroupBox> +#include <QDialogButtonBox> +#include <QHeaderView> +#include <QApplication> +#include <QDesktopWidget> + +LayerTreeDialog::LayerTreeDialog(PaneStack *stack, QWidget *parent) : + QDialog(parent), + m_paneStack(stack) +{ + setWindowTitle(tr("Layer Summary")); + + QGridLayout *grid = new QGridLayout; + setLayout(grid); + + QGroupBox *modelBox = new QGroupBox; + modelBox->setTitle(tr("Audio Data Sources")); + grid->addWidget(modelBox, 0, 0); + grid->setRowStretch(0, 15); + + QGridLayout *subgrid = new QGridLayout; + modelBox->setLayout(subgrid); + + subgrid->setSpacing(0); + subgrid->setMargin(5); + + m_modelView = new QTableView; + subgrid->addWidget(m_modelView); + + m_modelView->verticalHeader()->hide(); + m_modelView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); + m_modelView->setShowGrid(false); + + m_modelModel = new ModelDataModel(m_paneStack, true); + m_modelView->setModel(m_modelModel); + + QGroupBox *layerBox = new QGroupBox; + layerBox->setTitle(tr("Panes and Layers")); + grid->addWidget(layerBox, 1, 0); + grid->setRowStretch(1, 20); + + subgrid = new QGridLayout; + layerBox->setLayout(subgrid); + + subgrid->setSpacing(0); + subgrid->setMargin(5); + + m_layerView = new QTreeView; + m_layerView->header()->setResizeMode(QHeaderView::ResizeToContents); + subgrid->addWidget(m_layerView); + + m_layerModel = new LayerTreeModel(m_paneStack); + m_layerView->setModel(m_layerModel); + m_layerView->expandAll(); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + connect(bb, SIGNAL(rejected()), this, SLOT(reject())); + grid->addWidget(bb, 2, 0); + grid->setRowStretch(2, 0); + + QDesktopWidget *desktop = QApplication::desktop(); + QRect available = desktop->availableGeometry(); + + int width = available.width() / 2; + int height = available.height() / 3; + if (height < 370) { + if (available.height() > 500) height = 370; + } + if (width < 500) { + if (available.width() > 650) width = 500; + } + + resize(width, height); +} + +LayerTreeDialog::~LayerTreeDialog() +{ + delete m_layerModel; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LayerTreeDialog.h Wed Feb 27 11:59:42 2008 +0000 @@ -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 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 _LAYER_TREE_DIALOG_H_ +#define _LAYER_TREE_DIALOG_H_ + +#include <QDialog> + +class ModelDataModel; +class LayerTreeModel; +class PaneStack; +class QTreeView; +class QTableView; + +class LayerTreeDialog : public QDialog +{ + Q_OBJECT + +public: + LayerTreeDialog(PaneStack *stack, QWidget *parent = 0); + ~LayerTreeDialog(); + +protected: + PaneStack *m_paneStack; + ModelDataModel *m_modelModel; + QTableView *m_modelView; + LayerTreeModel *m_layerModel; + QTreeView *m_layerView; +}; + +#endif
--- a/widgets/PluginParameterBox.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PluginParameterBox.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -18,6 +18,7 @@ #include "AudioDial.h" #include "plugin/PluginXml.h" +#include "plugin/RealTimePluginInstance.h" // for PortHint stuff #include "base/RangeMapper.h" @@ -98,19 +99,31 @@ float deft = params[i].defaultValue; float value = m_plugin->getParameter(params[i].identifier); + int hint = PortHint::NoHint; + RealTimePluginInstance *rtpi = dynamic_cast<RealTimePluginInstance *> + (m_plugin); + if (rtpi) { + hint = rtpi->getParameterDisplayHint(i); + } + float qtz = 0.0; if (params[i].isQuantized) qtz = params[i].quantizeStep; + std::cerr << "PluginParameterBox: hint = " << hint << ", min = " << min << ", max = " + << max << ", qtz = " << qtz << std::endl; + std::vector<std::string> valueNames = params[i].valueNames; // construct an integer range int imin = 0, imax = 100; - if (qtz > 0.0) { - imax = int((max - min) / qtz); - } else { - qtz = (max - min) / 100.0; + if (!(hint & PortHint::Logarithmic)) { + if (qtz > 0.0) { + imax = int((max - min) / qtz); + } else { + qtz = (max - min) / 100.0; + } } //!!! would be nice to ensure the default value corresponds to @@ -162,12 +175,19 @@ dial->setMaximum(imax); dial->setPageStep(1); dial->setNotchesVisible((imax - imin) <= 12); - dial->setDefaultValue(lrintf((deft - min) / qtz)); - dial->setValue(lrintf((value - min) / qtz)); +//!!! dial->setDefaultValue(lrintf((deft - min) / qtz)); +// dial->setValue(lrintf((value - min) / qtz)); dial->setFixedWidth(32); dial->setFixedHeight(32); - dial->setRangeMapper(new LinearRangeMapper - (imin, imax, min, max, unit)); + RangeMapper *rm = 0; + if (hint & PortHint::Logarithmic) { + rm = new LogRangeMapper(imin, imax, min, max, unit); + } else { + rm = new LinearRangeMapper(imin, imax, min, max, unit); + } + dial->setRangeMapper(rm); + dial->setDefaultValue(rm->getPositionForValue(deft)); + dial->setValue(rm->getPositionForValue(value)); dial->setShowToolTip(true); connect(dial, SIGNAL(valueChanged(int)), this, SLOT(dialChanged(int))); @@ -178,7 +198,7 @@ spinbox->setMinimum(min); spinbox->setMaximum(max); spinbox->setSuffix(QString(" %1").arg(unit)); - spinbox->setSingleStep(qtz); + if (qtz != 0) spinbox->setSingleStep(qtz); spinbox->setValue(value); spinbox->setDecimals(4); connect(spinbox, SIGNAL(valueChanged(double)), @@ -238,6 +258,8 @@ newValue = min + ival * qtz; } + std::cerr << "PluginParameterBox::dialChanged: newValue = " << newValue << std::endl; + QDoubleSpinBox *spin = m_params[identifier].spin; if (spin) { spin->blockSignals(true); @@ -320,7 +342,11 @@ AudioDial *dial = m_params[identifier].dial; if (dial) { dial->blockSignals(true); - dial->setValue(ival); + if (dial->rangeMapper()) { + dial->setMappedValue(value); + } else { + dial->setValue(ival); + } dial->blockSignals(false); }
--- a/widgets/PluginParameterDialog.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PluginParameterDialog.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -440,7 +440,8 @@ } void -PluginParameterDialog::setCandidateInputModels(const QStringList &models) +PluginParameterDialog::setCandidateInputModels(const QStringList &models, + QString defaultModel) { m_inputModels->clear(); @@ -449,15 +450,17 @@ QString lastModel = settings.value("lastinputmodel").toString(); settings.endGroup(); + if (defaultModel == "") defaultModel = lastModel; + m_inputModels->show(); m_inputModelList = models; m_inputModels->addItems(TextAbbrev::abbreviate(models, 80)); m_inputModels->setCurrentIndex(0); - if (lastModel != "") { + if (defaultModel != "") { for (int i = 0; i < models.size(); ++i) { - if (lastModel == models[i]) { + if (defaultModel == models[i]) { m_inputModels->setCurrentIndex(i); m_currentInputModel = models[i]; break;
--- a/widgets/PluginParameterDialog.h Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PluginParameterDialog.h Wed Feb 27 11:59:42 2008 +0000 @@ -54,7 +54,8 @@ void setShowProcessingOptions(bool showWindowSize, bool showFrequencyDomainOptions); - void setCandidateInputModels(const QStringList &names); + void setCandidateInputModels(const QStringList &names, + QString defaultName); void setShowSelectionOnlyOption(bool show); Vamp::PluginBase *getPlugin() { return m_plugin; }
--- a/widgets/PropertyBox.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PropertyBox.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -29,9 +29,11 @@ #include "AudioDial.h" #include "LEDButton.h" +#include "IconLoader.h" #include "NotifyingCheckBox.h" #include "NotifyingComboBox.h" +#include "NotifyingPushButton.h" #include "ColourNameDialog.h" #include <QGridLayout> @@ -270,6 +272,7 @@ QString groupName = m_container->getPropertyGroupName(name); QString propertyLabel = m_container->getPropertyLabel(name); + QString iconName = m_container->getPropertyIconName(name); #ifdef DEBUG_PROPERTY_BOX std::cerr << "PropertyBox[" << this @@ -307,36 +310,45 @@ case PropertyContainer::ToggleProperty: { - NotifyingCheckBox *cb; + QAbstractButton *button = 0; if (have) { - cb = dynamic_cast<NotifyingCheckBox *>(m_propertyControllers[name]); - assert(cb); + button = dynamic_cast<QAbstractButton *>(m_propertyControllers[name]); + assert(button); } else { #ifdef DEBUG_PROPERTY_BOX std::cerr << "PropertyBox: creating new checkbox" << std::endl; #endif - cb = new NotifyingCheckBox(); - cb->setObjectName(name); - connect(cb, SIGNAL(stateChanged(int)), - this, SLOT(propertyControllerChanged(int))); - connect(cb, SIGNAL(mouseEntered()), + if (iconName != "") { + button = new NotifyingPushButton(); + button->setCheckable(true); + QIcon icon(IconLoader().load(iconName)); + button->setIcon(icon); + button->setObjectName(name); + button->setFixedSize(QSize(18, 18)); + } else { + button = new NotifyingCheckBox(); + button->setObjectName(name); + } + connect(button, SIGNAL(toggled(bool)), + this, SLOT(propertyControllerChanged(bool))); + connect(button, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); - connect(cb, SIGNAL(mouseLeft()), + connect(button, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); if (inGroup) { - cb->setToolTip(propertyLabel); - m_groupLayouts[groupName]->addWidget(cb); + button->setToolTip(propertyLabel); + m_groupLayouts[groupName]->addWidget(button); } else { - m_layout->addWidget(cb, row, 1, 1, 2); + m_layout->addWidget(button, row, 1, 1, 2); } - m_propertyControllers[name] = cb; + m_propertyControllers[name] = button; } - if (cb->isChecked() != (value > 0)) { - cb->blockSignals(true); - cb->setChecked(value > 0); - cb->blockSignals(false); + if (button->isChecked() != (value > 0)) { + button->blockSignals(true); + button->setChecked(value > 0); + button->blockSignals(false); } break; } @@ -577,6 +589,12 @@ } void +PropertyBox::propertyControllerChanged(bool on) +{ + propertyControllerChanged(on ? 1 : 0); +} + +void PropertyBox::propertyControllerChanged(int value) { QObject *obj = sender(); @@ -624,8 +642,8 @@ if (!newColour.isValid()) return; ColourNameDialog dialog(tr("Name New Colour"), - tr("Enter name for the new colour:"), - newColour, "", this); + 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 @@ -762,7 +780,7 @@ emit contextHelpChanged(tr("Toggle Playback of %1").arg(cname)); } else if (wname == "") { return; - } else if (dynamic_cast<NotifyingCheckBox *>(w)) { + } else if (dynamic_cast<QAbstractButton *>(w)) { emit contextHelpChanged(tr("Toggle %1 property of %2") .arg(wname).arg(cname)); } else {
--- a/widgets/PropertyBox.h Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PropertyBox.h Wed Feb 27 11:59:42 2008 +0000 @@ -54,6 +54,7 @@ protected slots: void propertyControllerChanged(int); + void propertyControllerChanged(bool); void playGainChanged(float); void playGainDialChanged(int);
--- a/widgets/PropertyStack.cpp Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/PropertyStack.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -114,17 +114,6 @@ shortName = QString("&%1 %2").arg(i + 1).arg(shortName); -#ifdef Q_WS_MAC - - // Qt 4.2 on OS/X doesn't show the icons in the tab bar, and - // I'm not sure why -- use labels instead - - addTab(box, shortName); - -#else - - // Icons on other platforms - QString iconName = container->getPropertyContainerIconName(); QIcon icon(IconLoader().load(iconName)); @@ -135,8 +124,6 @@ setTabToolTip(i, name); } -#endif - m_boxes.push_back(box); }
--- a/widgets/widgets.pro Mon Nov 19 15:50:37 2007 +0000 +++ b/widgets/widgets.pro Wed Feb 27 11:59:42 2008 +0000 @@ -23,6 +23,7 @@ KeyReference.h \ LabelCounterInputDialog.h \ LayerTree.h \ + LayerTreeDialog.h \ LEDButton.h \ ListInputDialog.h \ NotifyingCheckBox.h \ @@ -49,6 +50,7 @@ KeyReference.cpp \ LabelCounterInputDialog.cpp \ LayerTree.cpp \ + LayerTreeDialog.cpp \ LEDButton.cpp \ ListInputDialog.cpp \ NotifyingCheckBox.cpp \