Chris@919: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@919: Chris@919: /* Chris@919: Sonic Visualiser Chris@919: An audio file viewer and annotation editor. Chris@919: Centre for Digital Music, Queen Mary, University of London. Chris@919: Chris@919: This program is free software; you can redistribute it and/or Chris@919: modify it under the terms of the GNU General Public License as Chris@919: published by the Free Software Foundation; either version 2 of the Chris@919: License, or (at your option) any later version. See the file Chris@919: COPYING included with this distribution for more information. Chris@919: */ Chris@919: Chris@919: #ifndef VIEW_PROXY_H Chris@919: #define VIEW_PROXY_H Chris@919: Chris@1077: #include "layer/LayerGeometryProvider.h" Chris@919: Chris@1490: #include "data/model/AlignmentModel.h" Chris@1490: Chris@919: class ViewProxy : public LayerGeometryProvider Chris@919: { Chris@919: public: Chris@1490: /** Chris@1490: * Create a standard ViewProxy for the given view, mapping using Chris@1490: * the given scale factor. The scale factor is generally used with Chris@1490: * pixel-doubled "retina" Mac displays and is usually 1 elsewhere. Chris@1490: */ Chris@919: ViewProxy(View *view, int scaleFactor) : Chris@1266: m_view(view), m_scaleFactor(scaleFactor) { } Chris@919: Chris@1490: /** Chris@1490: * Create a re-aligning ViewProxy for the given view, mapping Chris@1490: * using the given scale factor. The scale factor is generally Chris@1490: * used with pixel-doubled "retina" Mac displays and is usually 1 Chris@1490: * elsewhere. Chris@1490: * Chris@1490: * Coordinates are mapped through the given alignment model, such Chris@1490: * that frame values passed from the caller are mapped "from Chris@1490: * reference" by that alignment before being used by the view or Chris@1490: * converted to pixel coordinates, and returned values are mapped Chris@1490: * back "to reference" before being passed back to the caller. Chris@1490: * Chris@1490: * This form of proxy may be created specially for rendering a Chris@1490: * single layer which comes from a different alignment to that of Chris@1490: * the rest of the containing view. Chris@1490: */ Chris@1490: ViewProxy(View *view, int scaleFactor, ModelId alignment) : Chris@1490: m_view(view), m_scaleFactor(scaleFactor), m_alignment(alignment) { } Chris@1490: Chris@1406: int getId() const override { Chris@1044: return m_view->getId(); Chris@1044: } Chris@1406: sv_frame_t getStartFrame() const override { Chris@1490: return alignToReference(m_view->getStartFrame()); Chris@919: } Chris@1406: sv_frame_t getCentreFrame() const override { Chris@1490: return alignToReference(m_view->getCentreFrame()); Chris@919: } Chris@1406: sv_frame_t getEndFrame() const override { Chris@1490: return alignToReference(m_view->getEndFrame()); Chris@919: } Chris@1406: int getXForFrame(sv_frame_t frame) const override { Chris@950: //!!! not actually correct, if frame lies between view's pixels Chris@1490: return m_scaleFactor * m_view->getXForFrame(alignFromReference(frame)); Chris@919: } Chris@1406: sv_frame_t getFrameForX(int x) const override { Chris@950: sv_frame_t f0 = m_view->getFrameForX(x / m_scaleFactor); Chris@1491: if (m_scaleFactor == 1) return alignToReference(f0); Chris@950: sv_frame_t f1 = m_view->getFrameForX((x / m_scaleFactor) + 1); Chris@1490: sv_frame_t f = f0 + ((f1 - f0) * (x % m_scaleFactor)) / m_scaleFactor; Chris@1490: return alignToReference(f); Chris@919: } Chris@1406: int getXForViewX(int viewx) const override { Chris@1030: return viewx * m_scaleFactor; Chris@1030: } Chris@1406: int getViewXForX(int x) const override { Chris@1030: return x / m_scaleFactor; Chris@1030: } Chris@1406: sv_frame_t getModelsStartFrame() const override { Chris@1490: return alignToReference(m_view->getModelsStartFrame()); Chris@919: } Chris@1406: sv_frame_t getModelsEndFrame() const override { Chris@1490: return alignToReference(m_view->getModelsEndFrame()); Chris@919: } Chris@1406: double getYForFrequency(double frequency, Chris@1266: double minFreq, double maxFreq, Chris@1406: bool logarithmic) const override { Chris@1266: return m_scaleFactor * Chris@1266: m_view->getYForFrequency(frequency, minFreq, maxFreq, logarithmic); Chris@919: } Chris@1406: double getFrequencyForY(double y, double minFreq, double maxFreq, Chris@1406: bool logarithmic) const override { Chris@1085: return m_view->getFrequencyForY Chris@950: (y / m_scaleFactor, minFreq, maxFreq, logarithmic); Chris@919: } Chris@1406: int getTextLabelHeight(const Layer *layer, QPainter &paint) const override { Chris@1266: return m_scaleFactor * m_view->getTextLabelHeight(layer, paint); Chris@919: } Chris@1406: bool getValueExtents(QString unit, double &min, double &max, Chris@1406: bool &log) const override { Chris@1266: return m_view->getValueExtents(unit, min, max, log); Chris@919: } Chris@1406: ZoomLevel getZoomLevel() const override { Chris@1326: ZoomLevel z = m_view->getZoomLevel(); Chris@1326: //!!! Chris@1329: // cerr << "getZoomLevel: from " << z << " to "; Chris@1326: if (z.zone == ZoomLevel::FramesPerPixel) { Chris@1326: z.level /= m_scaleFactor; Chris@1326: if (z.level < 1) { Chris@1326: z.level = 1; Chris@1326: } Chris@1326: } else { Chris@1326: //!!!??? Chris@1326: z.level *= m_scaleFactor; Chris@1326: } Chris@1329: // cerr << z << endl; Chris@1266: return z; Chris@919: } Chris@1406: QRect getPaintRect() const override { Chris@1266: QRect r = m_view->getPaintRect(); Chris@1266: return QRect(r.x() * m_scaleFactor, Chris@1266: r.y() * m_scaleFactor, Chris@1266: r.width() * m_scaleFactor, Chris@1266: r.height() * m_scaleFactor); Chris@919: } Chris@1406: QSize getPaintSize() const override { Chris@954: return getPaintRect().size(); Chris@954: } Chris@1406: int getPaintWidth() const override { Chris@954: return getPaintRect().width(); Chris@954: } Chris@1406: int getPaintHeight() const override { Chris@954: return getPaintRect().height(); Chris@954: } Chris@1406: bool hasLightBackground() const override { Chris@1266: return m_view->hasLightBackground(); Chris@919: } Chris@1406: QColor getForeground() const override { Chris@1266: return m_view->getForeground(); Chris@919: } Chris@1406: QColor getBackground() const override { Chris@1266: return m_view->getBackground(); Chris@919: } Chris@1406: ViewManager *getViewManager() const override { Chris@1266: return m_view->getViewManager(); Chris@919: } Chris@1266: Chris@1406: bool shouldIlluminateLocalFeatures(const Layer *layer, Chris@1406: QPoint &point) const override { Chris@954: QPoint p; Chris@1266: bool should = m_view->shouldIlluminateLocalFeatures(layer, p); Chris@954: point = QPoint(p.x() * m_scaleFactor, p.y() * m_scaleFactor); Chris@954: return should; Chris@919: } Chris@954: Chris@1406: bool shouldShowFeatureLabels() const override { Chris@1266: return m_view->shouldShowFeatureLabels(); Chris@919: } Chris@919: Chris@1406: void drawMeasurementRect(QPainter &p, const Layer *layer, Chris@1406: QRect rect, bool focus) const override { Chris@1266: m_view->drawMeasurementRect(p, layer, rect, focus); Chris@919: } Chris@919: Chris@1406: void updatePaintRect(QRect r) override { Chris@1030: m_view->update(r.x() / m_scaleFactor, Chris@1030: r.y() / m_scaleFactor, Chris@1030: r.width() / m_scaleFactor, Chris@1030: r.height() / m_scaleFactor); Chris@1030: } Chris@1401: Chris@1401: /** Chris@1401: * Scale up a size in pixels for a hi-dpi display without pixel Chris@1401: * doubling. This is like ViewManager::scalePixelSize, but taking Chris@1401: * and returning floating-point values rather than integer Chris@1401: * pixels. It is also a little more conservative - it never Chris@1401: * shrinks the size, it can only increase or leave it unchanged. Chris@1401: */ Chris@1406: double scaleSize(double size) const override { Chris@1401: return m_view->scaleSize(size * m_scaleFactor); Chris@1401: } Chris@1401: Chris@1401: /** Chris@1402: * Integer version of scaleSize. Chris@1402: */ Chris@1406: int scalePixelSize(int size) const override { Chris@1402: return m_view->scalePixelSize(size * m_scaleFactor); Chris@1402: } Chris@1402: Chris@1402: /** Chris@1401: * Scale up pen width for a hi-dpi display without pixel doubling. Chris@1401: * This is like scaleSize except that it also scales the Chris@1401: * zero-width case. Chris@1401: */ Chris@1406: double scalePenWidth(double width) const override { Chris@1401: if (width <= 0) { // zero-width pen, produce a scaled one-pixel pen Chris@1401: width = 1; Chris@1401: } Chris@1401: width *= sqrt(double(m_scaleFactor)); Chris@1401: return m_view->scalePenWidth(width); Chris@1401: } Chris@1401: Chris@1401: /** Chris@1401: * Apply scalePenWidth to a pen. Chris@1401: */ Chris@1406: QPen scalePen(QPen pen) const override { Chris@1401: return QPen(pen.color(), scalePenWidth(pen.width())); Chris@1401: } Chris@1030: Chris@1406: View *getView() override { return m_view; } Chris@1406: const View *getView() const override { return m_view; } Chris@919: Chris@919: private: Chris@919: View *m_view; Chris@919: int m_scaleFactor; Chris@1490: ModelId m_alignment; Chris@1490: Chris@1490: sv_frame_t alignToReference(sv_frame_t frame) const { Chris@1490: if (auto am = ModelById::getAs(m_alignment)) { Chris@1490: return am->toReference(frame); Chris@1490: } else { Chris@1490: return frame; Chris@1490: } Chris@1490: } Chris@1490: Chris@1490: sv_frame_t alignFromReference(sv_frame_t frame) const { Chris@1490: if (auto am = ModelById::getAs(m_alignment)) { Chris@1490: return am->fromReference(frame); Chris@1490: } else { Chris@1490: return frame; Chris@1490: } Chris@1490: } Chris@919: }; Chris@919: Chris@919: #endif