Chris@127: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@127: 
Chris@127: /*
Chris@127:     Sonic Visualiser
Chris@127:     An audio file viewer and annotation editor.
Chris@127:     Centre for Digital Music, Queen Mary, University of London.
Chris@182:     This file copyright 2006 Chris Cannam and QMUL.
Chris@127:     
Chris@127:     This program is free software; you can redistribute it and/or
Chris@127:     modify it under the terms of the GNU General Public License as
Chris@127:     published by the Free Software Foundation; either version 2 of the
Chris@127:     License, or (at your option) any later version.  See the file
Chris@127:     COPYING included with this distribution for more information.
Chris@127: */
Chris@127: 
Chris@127: #include "Layer.h"
Chris@128: #include "view/View.h"
Chris@128: #include "data/model/Model.h"
Chris@376: #include "widgets/CommandHistory.h"
Chris@127: 
Chris@127: #include <iostream>
Chris@127: 
Chris@131: #include <QMutexLocker>
Chris@267: #include <QMouseEvent>
Chris@316: #include <QTextStream>
Chris@131: 
Chris@326: #include <QDomDocument>
Chris@326: #include <QDomElement>
Chris@326: #include <QDomNamedNodeMap>
Chris@326: #include <QDomAttr>
Chris@326: 
Chris@131: #include "LayerFactory.h"
Chris@128: #include "base/PlayParameterRepository.h"
Chris@127: 
Chris@272: #include <cmath>
Chris@272: 
Chris@267: Layer::Layer() :
Chris@283:     m_haveDraggingRect(false),
Chris@283:     m_haveCurrentMeasureRect(false)
Chris@127: {
Chris@127: }
Chris@127: 
Chris@127: Layer::~Layer()
Chris@127: {
Chris@587: //    SVDEBUG << "Layer::~Layer(" << this << ")" << endl;
Chris@127: }
Chris@127: 
Chris@320: void
Chris@1469: Layer::connectSignals(ModelId modelId)
Chris@320: {
Chris@1469:     auto model = ModelById::get(modelId);
Chris@1469:     if (!model) return;
Chris@1469:     
Chris@1481:     connect(model.get(), SIGNAL(modelChanged(ModelId)),
Chris@1481:             this, SIGNAL(modelChanged(ModelId)));
Chris@320: 
Chris@1481:     connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
Chris@1481:             this, SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
Chris@320: 
Chris@1481:     connect(model.get(), SIGNAL(completionChanged(ModelId)),
Chris@1481:             this, SIGNAL(modelCompletionChanged(ModelId)));
Chris@320: 
Chris@1481:     connect(model.get(), SIGNAL(alignmentCompletionChanged(ModelId)),
Chris@1481:             this, SIGNAL(modelAlignmentCompletionChanged(ModelId)));
Chris@320: }
Chris@320: 
Chris@1489: ModelId
Chris@1489: Layer::getSourceModel() const
Chris@1489: {
Chris@1489:     ModelId sourceId;
Chris@1489:     auto model = ModelById::get(getModel());
Chris@1489:     while (model && !model->getSourceModel().isNone()) {
Chris@1489:         sourceId = model->getSourceModel();
Chris@1489:         model = ModelById::get(sourceId);
Chris@1489:     }
Chris@1489:     return sourceId;
Chris@1489: }
Chris@1489: 
Chris@127: QString
Chris@127: Layer::getPropertyContainerIconName() const
Chris@127: {
Chris@127:     return LayerFactory::getInstance()->getLayerIconName
Chris@1266:         (LayerFactory::getInstance()->getLayerType(this));
Chris@127: }
Chris@127: 
Chris@363: void
Chris@363: Layer::setPresentationName(QString name)
Chris@363: {
Chris@363:     m_presentationName = name;
Chris@363: }
Chris@363: 
Chris@1585: bool
Chris@1585: Layer::isPresentationNameSet() const
Chris@1585: {
Chris@1585:     return (m_presentationName != "");
Chris@1585: }
Chris@1585: 
Chris@127: QString
Chris@127: Layer::getLayerPresentationName() const
Chris@127: {
Chris@363:     if (m_presentationName != "") return m_presentationName;
Chris@203: 
Chris@203:     LayerFactory *factory = LayerFactory::getInstance();
Chris@203:     QString layerName = factory->getLayerPresentationName
Chris@203:         (factory->getLayerType(this));
Chris@203: 
Chris@127:     QString modelName;
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (model) modelName = model->objectName();
Chris@1266:             
Chris@127:     QString text;
Chris@127:     if (modelName != "") {
Chris@1266:         text = QString("%1: %2").arg(modelName).arg(layerName);
Chris@127:     } else {
Chris@1266:         text = layerName;
Chris@127:     }
Chris@1266:         
Chris@127:     return text;
Chris@127: }
Chris@127: 
Chris@127: void
Chris@127: Layer::setObjectName(const QString &name)
Chris@127: {
Chris@127:     QObject::setObjectName(name);
Chris@127:     emit layerNameChanged();
Chris@127: }
Chris@127: 
Chris@1480: std::shared_ptr<PlayParameters>
Chris@127: Layer::getPlayParameters() 
Chris@127: {
Chris@1470:     return PlayParameterRepository::getInstance()->getPlayParameters
Chris@1470:         (getModel().untyped);
Chris@127: }
Chris@127: 
Chris@127: void
Chris@918: Layer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
Chris@131: {
Chris@131:     const void *vv = (const void *)v;
Chris@131:     QMutexLocker locker(&m_dormancyMutex);
Chris@131:     m_dormancy[vv] = dormant;
Chris@131: }
Chris@131: 
Chris@131: bool
Chris@918: Layer::isLayerDormant(const LayerGeometryProvider *v) const
Chris@131: {
Chris@131:     const void *vv = (const void *)v;
Chris@131:     QMutexLocker locker(&m_dormancyMutex);
Chris@131:     if (m_dormancy.find(vv) == m_dormancy.end()) return false;
Chris@131:     return m_dormancy.find(vv)->second;
Chris@131: }
Chris@131: 
Chris@131: void
Chris@918: Layer::showLayer(LayerGeometryProvider *view, bool show)
Chris@127: {
Chris@127:     setLayerDormant(view, !show);
Chris@127:     emit layerParametersChanged();
Chris@127: }
Chris@127: 
Chris@260: bool
Chris@918: Layer::getXScaleValue(const LayerGeometryProvider *v, int x, double &value, QString &unit) const
Chris@260: {
Chris@260:     if (!hasTimeXAxis()) return false;
Chris@260: 
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (!model) return false;
Chris@260: 
Chris@1469:     value = double(v->getFrameForX(x)) / model->getSampleRate();
Chris@260:     unit = "s";
Chris@260:     return true;
Chris@260: }
Chris@260: 
Chris@268: bool
Chris@918: Layer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
Chris@904:                            double &diff, QString &unit) const
Chris@274: {
Chris@904:     double v0, v1;
Chris@274:     if (!getYScaleValue(v, y0, v0, unit) ||
Chris@274:         !getYScaleValue(v, y1, v1, unit)) {
Chris@274:         diff = 0.f;
Chris@274:         return false;
Chris@274:     }
Chris@904:     diff = fabs(v1 - v0);
Chris@274:     return true;
Chris@274: }
Chris@274: 
Chris@904: sv_frame_t
Chris@918: Layer::alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
Chris@359: {
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (model && !model->getAlignmentReference().isNone()) {
Chris@1469:         return model->alignToReference(frame);
Chris@359:     } else {
Chris@918:         return v->getView()->alignToReference(frame);
Chris@359:     }
Chris@359: }
Chris@359: 
Chris@904: sv_frame_t
Chris@918: Layer::alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
Chris@359: {
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (model && !model->getAlignmentReference().isNone()) {
Chris@1469:         return model->alignFromReference(frame);
Chris@359:     } else {
Chris@918:         return v->getView()->alignFromReference(frame);
Chris@359:     }
Chris@359: }
Chris@359: 
Chris@274: bool
Chris@918: Layer::clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
Chris@360: {
Chris@360:     // Notes on pasting to an aligned layer:
Chris@360:     // 
Chris@360:     // Each point may have a reference frame that may differ from the
Chris@360:     // point's given frame (in its source model).  If it has no
Chris@360:     // reference frame, we have to assume the source model was not
Chris@360:     // aligned or was the reference model: when cutting or copying
Chris@360:     // points from a layer, we must always set their reference frame
Chris@360:     // correctly if we are aligned.
Chris@360:     // 
Chris@360:     // When pasting:
Chris@360:     // - if point's reference and aligned frames differ:
Chris@360:     //   - if this layer is aligned:
Chris@360:     //     - if point's aligned frame matches this layer's aligned version
Chris@360:     //       of point's reference frame:
Chris@360:     //       - we can paste at reference frame or our frame
Chris@360:     //     - else
Chris@360:     //       - we can paste at reference frame, result of aligning reference
Chris@360:     //         frame in our model, or literal source frame
Chris@360:     //   - else
Chris@360:     //     - we can paste at reference (our) frame, or literal source frame
Chris@360:     // - else
Chris@360:     //   - if this layer is aligned:
Chris@360:     //     - we can paste at reference (point's only available) frame,
Chris@360:     //       or result of aligning reference frame in our model
Chris@360:     //   - else
Chris@360:     //     - we can only paste at reference frame
Chris@360:     // 
Chris@360:     // Which of these alternatives are useful?
Chris@360:     //
Chris@360:     // Example: we paste between two tracks that are aligned to the
Chris@360:     // same reference, and the points are at 10s and 20s in the source
Chris@360:     // track, corresponding to 5s and 10s in the reference but 20s and
Chris@360:     // 30s in the target track.
Chris@360:     // 
Chris@360:     // The obvious default is to paste at 20s and 30s; if we aren't
Chris@360:     // doing that, would it be better to paste at 5s and 10s or at 10s
Chris@360:     // and 20s?  We probably don't ever want to do the former, do we?
Chris@360:     // We either want to be literal all the way through, or aligned
Chris@360:     // all the way through.
Chris@360: 
Chris@1423:     for (EventVector::const_iterator i = clip.getPoints().begin();
Chris@360:          i != clip.getPoints().end(); ++i) {
Chris@360: 
Chris@360:         // In principle, we want to know whether the aligned version
Chris@360:         // of the reference frame in our layer is the same as the
Chris@360:         // source frame contained in the clipboard point.  However,
Chris@360:         // because of rounding during alignment, that won't
Chris@360:         // necessarily be the case even if the clipboard point came
Chris@360:         // from our layer!  What we need to check is whether, if we
Chris@360:         // aligned the clipboard point's frame back to the reference
Chris@360:         // using this layer's alignment, we would obtain the same
Chris@360:         // reference frame as that for the clipboard point.
Chris@360: 
Chris@360:         // What if the clipboard point has no reference frame?  Then
Chris@360:         // we have to treat it as having its own frame as the
Chris@360:         // reference (i.e. having been copied from the reference
Chris@360:         // model).
Chris@360:         
Chris@905:         sv_frame_t sourceFrame = i->getFrame();
Chris@905:         sv_frame_t referenceFrame = sourceFrame;
Chris@1423:         if (i->hasReferenceFrame()) {
Chris@360:             referenceFrame = i->getReferenceFrame();
Chris@360:         }
Chris@905:         sv_frame_t myMappedFrame = alignToReference(v, sourceFrame);
Chris@360: 
Chris@1423: //        cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->hasReferenceFrame() << "), myMappedFrame = " << myMappedFrame << endl;
Chris@360: 
Chris@360:         if (myMappedFrame != referenceFrame) return true;
Chris@360:     }
Chris@360: 
Chris@360:     return false;
Chris@360: }
Chris@360: 
Chris@360: bool
Chris@268: Layer::MeasureRect::operator<(const MeasureRect &mr) const
Chris@268: {
Chris@268:     if (haveFrames) {
Chris@268:         if (startFrame == mr.startFrame) {
Chris@268:             if (endFrame != mr.endFrame) {
Chris@268:                 return endFrame < mr.endFrame;
Chris@268:             }
Chris@268:         } else {
Chris@268:             return startFrame < mr.startFrame;
Chris@268:         }
Chris@268:     } else {
Chris@268:         if (pixrect.x() == mr.pixrect.x()) {
Chris@268:             if (pixrect.width() != mr.pixrect.width()) {
Chris@268:                 return pixrect.width() < mr.pixrect.width();
Chris@268:             }
Chris@268:         } else {
Chris@268:             return pixrect.x() < mr.pixrect.x();
Chris@268:         }
Chris@268:     }
Chris@268: 
Chris@268:     // the two rects are equal in x and width
Chris@268: 
Chris@268:     if (pixrect.y() == mr.pixrect.y()) {
Chris@268:         return pixrect.height() < mr.pixrect.height();
Chris@268:     } else {
Chris@268:         return pixrect.y() < mr.pixrect.y();
Chris@268:     }
Chris@268: }
Chris@268: 
Chris@316: void
Chris@316: Layer::MeasureRect::toXml(QTextStream &stream, QString indent) const
Chris@269: {
Chris@316:     stream << indent;
Chris@316:     stream << QString("<measurement ");
Chris@269: 
Chris@269:     if (haveFrames) {
Chris@316:         stream << QString("startFrame=\"%1\" endFrame=\"%2\" ")
Chris@269:             .arg(startFrame).arg(endFrame);
Chris@269:     } else {
Chris@316:         stream << QString("startX=\"%1\" endX=\"%2\" ")
Chris@316:             .arg(pixrect.x()).arg(pixrect.x() << pixrect.width());
Chris@269:     }
Chris@269: 
Chris@316:     stream << QString("startY=\"%1\" endY=\"%2\"/>\n")
Chris@273:         .arg(startY).arg(endY);
Chris@269: }
Chris@269: 
Chris@269: void
Chris@269: Layer::addMeasurementRect(const QXmlAttributes &attributes)
Chris@269: {
Chris@269:     MeasureRect rect;
Chris@269:     QString fs = attributes.value("startFrame");
Chris@273:     int x0 = 0, x1 = 0;
Chris@269:     if (fs != "") {
Chris@806:         rect.startFrame = fs.toInt();
Chris@806:         rect.endFrame = attributes.value("endFrame").toInt();
Chris@269:         rect.haveFrames = true;
Chris@269:     } else {
Chris@269:         x0 = attributes.value("startX").toInt();
Chris@269:         x1 = attributes.value("endX").toInt();
Chris@269:         rect.haveFrames = false;
Chris@269:     }
Chris@273:     rect.startY = attributes.value("startY").toDouble();
Chris@273:     rect.endY = attributes.value("endY").toDouble();
Chris@273:     rect.pixrect = QRect(x0, 0, x1 - x0, 0);
Chris@269:     addMeasureRectToSet(rect);
Chris@269: }
Chris@269: 
Chris@269: QString
Chris@268: Layer::AddMeasurementRectCommand::getName() const
Chris@268: {
Chris@268:     return tr("Make Measurement");
Chris@268: }
Chris@268: 
Chris@268: void
Chris@268: Layer::AddMeasurementRectCommand::execute()
Chris@268: {
Chris@269:     m_layer->addMeasureRectToSet(m_rect);
Chris@268: }
Chris@268: 
Chris@268: void
Chris@268: Layer::AddMeasurementRectCommand::unexecute()
Chris@268: {
Chris@269:     m_layer->deleteMeasureRectFromSet(m_rect);
Chris@268: }
Chris@268: 
Chris@283: QString
Chris@283: Layer::DeleteMeasurementRectCommand::getName() const
Chris@283: {
Chris@283:     return tr("Delete Measurement");
Chris@283: }
Chris@283: 
Chris@283: void
Chris@283: Layer::DeleteMeasurementRectCommand::execute()
Chris@283: {
Chris@283:     m_layer->deleteMeasureRectFromSet(m_rect);
Chris@283: }
Chris@283: 
Chris@283: void
Chris@283: Layer::DeleteMeasurementRectCommand::unexecute()
Chris@283: {
Chris@283:     m_layer->addMeasureRectToSet(m_rect);
Chris@283: }
Chris@283: 
Chris@267: void
Chris@918: Layer::measureStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@267: {
Chris@1434:     m_draggingRect.haveFrames = hasTimeXAxis();
Chris@1434: 
Chris@1434:     // NB if haveFrames, then pixrect x and width will be rewritten on
Chris@1434:     // every paint according to the current locations of the
Chris@1434:     // definitive frame values. So we should set the start frame value
Chris@1434:     // once on measureStart, and then not modify it on drag (to avoid
Chris@1434:     // drift from repeated conversion back and forth).
Chris@1434:     
Chris@1434:     m_draggingRect.pixrect = QRect(e->x(), e->y(), 0, 0);
Chris@1434: 
Chris@1434:     if (m_draggingRect.haveFrames) {
Chris@1434:         m_draggingRect.startFrame = v->getFrameForX(e->x());
Chris@1434:         m_draggingRect.endFrame = v->getFrameForX(e->x());
Chris@1434:     }
Chris@1434: 
Chris@1434:     setMeasureRectYCoord(v, m_draggingRect, true, e->y());
Chris@1434:     setMeasureRectYCoord(v, m_draggingRect, false, e->y());
Chris@1434: 
Chris@267:     m_haveDraggingRect = true;
Chris@267: }
Chris@267: 
Chris@267: void
Chris@918: Layer::measureDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@267: {
Chris@267:     if (!m_haveDraggingRect) return;
Chris@268: 
Chris@1434:     m_draggingRect.pixrect.setHeight(e->y() - m_draggingRect.pixrect.y());
Chris@1434: 
Chris@1434:     if (m_draggingRect.haveFrames) {
Chris@1434:         m_draggingRect.endFrame = v->getFrameForX(e->x());
Chris@1434:     } else {
Chris@1434:         m_draggingRect.pixrect.setWidth(e->x() - m_draggingRect.pixrect.x());
Chris@1434:     }
Chris@1434: 
Chris@1434:     setMeasureRectYCoord(v, m_draggingRect, false, e->y());
Chris@267: }
Chris@267: 
Chris@267: void
Chris@918: Layer::measureEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@267: {
Chris@267:     if (!m_haveDraggingRect) return;
Chris@267:     measureDrag(v, e);
Chris@283: 
Chris@283:     if (!m_draggingRect.pixrect.isNull()) {
Chris@283:         CommandHistory::getInstance()->addCommand
Chris@283:             (new AddMeasurementRectCommand(this, m_draggingRect));
Chris@283:     }
Chris@268: 
Chris@267:     m_haveDraggingRect = false;
Chris@267: }
Chris@267: 
Chris@267: void
Chris@918: Layer::measureDoubleClick(LayerGeometryProvider *, QMouseEvent *)
Chris@280: {
Chris@283:     // nothing, in the base class
Chris@283: }
Chris@283: 
Chris@283: void
Chris@283: Layer::deleteCurrentMeasureRect()
Chris@283: {
Chris@283:     if (!m_haveCurrentMeasureRect) return;
Chris@283:     
Chris@283:     MeasureRectSet::const_iterator focusRectItr =
Chris@283:         findFocusedMeasureRect(m_currentMeasureRectPoint);
Chris@283: 
Chris@283:     if (focusRectItr == m_measureRects.end()) return;
Chris@283: 
Chris@283:     CommandHistory::getInstance()->addCommand
Chris@283:         (new DeleteMeasurementRectCommand(this, *focusRectItr));
Chris@280: }
Chris@280: 
Chris@280: void
Chris@918: Layer::paintMeasurementRects(LayerGeometryProvider *v, QPainter &paint,
Chris@272:                              bool showFocus, QPoint focusPoint) const
Chris@267: {
Chris@273:     updateMeasurePixrects(v);
Chris@272: 
Chris@272:     MeasureRectSet::const_iterator focusRectItr = m_measureRects.end();
Chris@272: 
Chris@267:     if (m_haveDraggingRect) {
Chris@272: 
Chris@270:         paintMeasurementRect(v, paint, m_draggingRect, true);
Chris@272: 
Chris@272:     } else if (showFocus) {
Chris@272: 
Chris@272:         focusRectItr = findFocusedMeasureRect(focusPoint);
Chris@267:     }
Chris@267: 
Chris@283:     m_haveCurrentMeasureRect = false;
Chris@283: 
Chris@268:     for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 
Chris@268:          i != m_measureRects.end(); ++i) {
Chris@283: 
Chris@283:         bool focused = (i == focusRectItr);
Chris@283:         paintMeasurementRect(v, paint, *i, focused);
Chris@283: 
Chris@283:         if (focused) {
Chris@283:             m_haveCurrentMeasureRect = true;
Chris@283:             m_currentMeasureRectPoint = focusPoint;
Chris@283:         }
Chris@267:     }
Chris@267: }
Chris@267: 
Chris@272: bool
Chris@918: Layer::nearestMeasurementRectChanged(LayerGeometryProvider *v, QPoint prev, QPoint now) const
Chris@272: {
Chris@273:     updateMeasurePixrects(v);
Chris@272:     
Chris@272:     MeasureRectSet::const_iterator i0 = findFocusedMeasureRect(prev);
Chris@272:     MeasureRectSet::const_iterator i1 = findFocusedMeasureRect(now);
Chris@272: 
Chris@272:     return (i0 != i1);
Chris@272: }
Chris@272: 
Chris@272: void
Chris@918: Layer::updateMeasurePixrects(LayerGeometryProvider *v) const
Chris@272: {
Chris@905:     sv_frame_t sf = v->getStartFrame();
Chris@905:     sv_frame_t ef = v->getEndFrame();
Chris@272: 
Chris@272:     for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 
Chris@272:          i != m_measureRects.end(); ++i) {
Chris@272: 
Chris@273:         // This logic depends on the fact that if one measure rect in
Chris@273:         // a layer has frame values, they all will.  That is in fact
Chris@273:         // the case, because haveFrames is based on whether the layer
Chris@273:         // hasTimeXAxis() or not.  Measure rect ordering in the rect
Chris@273:         // set wouldn't work correctly either, if haveFrames could
Chris@273:         // vary.
Chris@272: 
Chris@273:         if (i->haveFrames) {
Chris@273:             if (i->startFrame >= ef) break;
Chris@273:             if (i->endFrame <= sf) continue;
Chris@273:         }
Chris@272: 
Chris@273:         int x0 = i->pixrect.x();
Chris@273:         int x1 = x0 + i->pixrect.width();
Chris@273: 
Chris@273:         if (i->haveFrames) {
Chris@273:             if (i->startFrame >= v->getStartFrame()) {
Chris@273:                 x0 = v->getXForFrame(i->startFrame);
Chris@273:             }
Chris@806:             if (i->endFrame <= int(v->getEndFrame())) {
Chris@273:                 x1 = v->getXForFrame(i->endFrame);
Chris@273:             }
Chris@272:         }
Chris@272:         
Chris@273:         i->pixrect = QRect(x0, i->pixrect.y(), x1 - x0, i->pixrect.height());
Chris@273: 
Chris@273:         updateMeasureRectYCoords(v, *i);
Chris@273:     }
Chris@273: }
Chris@273: 
Chris@273: void
Chris@918: Layer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
Chris@273: {
Chris@918:     int y0 = int(lrint(r.startY * v->getPaintHeight()));
Chris@918:     int y1 = int(lrint(r.endY * v->getPaintHeight()));
Chris@273:     r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
Chris@273: }
Chris@273: 
Chris@273: void
Chris@918: Layer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const
Chris@273: {
Chris@273:     if (start) {
Chris@918:         r.startY = double(y) / double(v->getPaintHeight());
Chris@273:         r.endY = r.startY;
Chris@273:     } else {
Chris@918:         r.endY = double(y) / double(v->getPaintHeight());
Chris@272:     }
Chris@272: }
Chris@272: 
Chris@283: void
Chris@918: Layer::setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const
Chris@283: {
Chris@283:     r.pixrect = pixrect;
Chris@283:     r.haveFrames = hasTimeXAxis();
Chris@283:     if (r.haveFrames) {
Chris@283:         r.startFrame = v->getFrameForX(pixrect.x());
Chris@283:         r.endFrame = v->getFrameForX(pixrect.x() + pixrect.width());
Chris@283:     }
Chris@283:     setMeasureRectYCoord(v, r, true, pixrect.y());
Chris@283:     setMeasureRectYCoord(v, r, false, pixrect.y() + pixrect.height());
Chris@283: }
Chris@283: 
Chris@272: Layer::MeasureRectSet::const_iterator
Chris@272: Layer::findFocusedMeasureRect(QPoint focusPoint) const
Chris@272: {
Chris@904:     double frDist = 0;
Chris@272:     MeasureRectSet::const_iterator focusRectItr = m_measureRects.end();
Chris@272: 
Chris@272:     for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 
Chris@272:          i != m_measureRects.end(); ++i) {
Chris@272: 
Chris@272:         if (!i->pixrect.adjusted(-2, -2, 2, 2).contains(focusPoint)) continue;
Chris@272: 
Chris@272:         int cx = i->pixrect.x() + i->pixrect.width()/2;
Chris@272:         int cy = i->pixrect.y() + i->pixrect.height()/2;
Chris@272:         int xd = focusPoint.x() - cx;
Chris@272:         int yd = focusPoint.y() - cy;
Chris@272:         
Chris@904:         double d = sqrt(double(xd * xd + yd * yd));
Chris@272:         
Chris@272:         if (focusRectItr == m_measureRects.end() || d < frDist) {
Chris@272:             focusRectItr = i;
Chris@272:             frDist = d;
Chris@272:         }
Chris@272:     }
Chris@272: 
Chris@272:     return focusRectItr;
Chris@272: }
Chris@272: 
Chris@268: void
Chris@918: Layer::paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint,
Chris@270:                             const MeasureRect &r, bool focus) const
Chris@268: {
Chris@268:     if (r.haveFrames) {
Chris@268:         
Chris@268:         int x0 = -1;
Chris@918:         int x1 = v->getPaintWidth() + 1;
Chris@268:         
Chris@268:         if (r.startFrame >= v->getStartFrame()) {
Chris@268:             x0 = v->getXForFrame(r.startFrame);
Chris@268:         }
Chris@806:         if (r.endFrame <= v->getEndFrame()) {
Chris@268:             x1 = v->getXForFrame(r.endFrame);
Chris@268:         }
Chris@268:         
Chris@272:         QRect pr = QRect(x0, r.pixrect.y(), x1 - x0, r.pixrect.height());
Chris@268:         r.pixrect = pr;
Chris@268:     }
Chris@274: 
Chris@274:     v->drawMeasurementRect(paint, this, r.pixrect.normalized(), focus);
Chris@268: }
Chris@268: 
Chris@1315: bool
Chris@1315: Layer::valueExtentsMatchMine(LayerGeometryProvider *v) const
Chris@1315: {
Chris@1315:     double min, min_;
Chris@1315:     double max, max_;
Chris@1315:     bool logarithmic, logarithmic_;
Chris@1315:     QString unit;
Chris@1315: 
Chris@1315:     if (!getValueExtents(min_, max_, logarithmic_, unit)) {
Chris@1315:         return false;
Chris@1315:     }
Chris@1315: 
Chris@1537:     if (!v->getVisibleExtentsForUnit(unit, min, max, logarithmic)) {
Chris@1315:         return false;
Chris@1315:     }
Chris@1315: 
Chris@1315:     if (min != min_ ||
Chris@1315:         max != max_ ||
Chris@1315:         logarithmic != logarithmic_) {
Chris@1315:         return false;
Chris@1315:     }
Chris@1315: 
Chris@1315:     return true;
Chris@1315: }
Chris@1315: 
Chris@316: void
Chris@316: Layer::toXml(QTextStream &stream,
Chris@316:              QString indent, QString extraAttributes) const
Chris@268: {
Chris@316:     stream << indent;
Chris@268: 
Chris@363:     if (m_presentationName != "") {
Chris@363:         extraAttributes = QString("%1 presentationName=\"%2\"")
Chris@363:             .arg(extraAttributes).arg(encodeEntities(m_presentationName));
Chris@363:     }
Chris@363: 
Chris@1469:     int modelExportId = -1;
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (model) modelExportId = model->getExportId();
Chris@1469:     
Chris@316:     stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5")
Chris@1266:         .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName
Chris@268:                             (LayerFactory::getInstance()->getLayerType(this))))
Chris@1439:         .arg(getExportId())
Chris@1266:         .arg(encodeEntities(objectName()))
Chris@1469:         .arg(modelExportId)
Chris@1266:         .arg(extraAttributes);
Chris@268: 
Chris@269:     if (m_measureRects.empty()) {
Chris@316:         stream << QString("/>\n");
Chris@316:         return;
Chris@269:     }
Chris@269: 
Chris@316:     stream << QString(">\n");
Chris@269: 
Chris@269:     for (MeasureRectSet::const_iterator i = m_measureRects.begin();
Chris@269:          i != m_measureRects.end(); ++i) {
Chris@316:         i->toXml(stream, indent + "  ");
Chris@269:     }
Chris@269: 
Chris@316:     stream << QString("</layer>\n");
Chris@268: }
Chris@269: 
Chris@316: void
Chris@316: Layer::toBriefXml(QTextStream &stream,
Chris@316:                   QString indent, QString extraAttributes) const
Chris@269: {
Chris@316:     stream << indent;
Chris@269: 
Chris@363:     if (m_presentationName != "") {
Chris@363:         extraAttributes = QString("%1 presentationName=\"%2\"")
Chris@363:             .arg(extraAttributes).arg(encodeEntities(m_presentationName));
Chris@363:     }
Chris@363: 
Chris@1469:     int modelExportId = -1;
Chris@1469:     auto model = ModelById::get(getModel());
Chris@1469:     if (model) modelExportId = model->getExportId();
Chris@1469: 
Chris@316:     stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5/>\n")
Chris@1266:         .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName
Chris@269:                             (LayerFactory::getInstance()->getLayerType(this))))
Chris@1439:         .arg(getExportId())
Chris@1266:         .arg(encodeEntities(objectName()))
Chris@1469:         .arg(modelExportId)
Chris@269:         .arg(extraAttributes);
Chris@269: }
Chris@269: