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@127: This file copyright 2006 Chris Cannam. 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@226: #ifndef _VIEW_H_ Chris@226: #define _VIEW_H_ Chris@127: Chris@127: #include Chris@127: #include Chris@127: Chris@127: #include "base/ZoomConstraint.h" Chris@127: #include "base/PropertyContainer.h" Chris@128: #include "ViewManager.h" Chris@127: #include "base/XmlExportable.h" Chris@127: Chris@127: // #define DEBUG_VIEW_WIDGET_PAINT 1 Chris@127: Chris@127: class Layer; Chris@127: class ViewPropertyContainer; Chris@127: Chris@127: #include Chris@127: Chris@127: /** Chris@127: * View is the base class of widgets that display one or more Chris@127: * overlaid views of data against a horizontal time scale. Chris@127: * Chris@127: * A View may have any number of attached Layers, each of which Chris@127: * is expected to have one data Model (although multiple views may Chris@127: * share the same model). Chris@127: * Chris@127: * A View may be panned in time and zoomed, although the Chris@127: * mechanisms for doing so (as well as any other operations and Chris@127: * properties available) depend on the subclass. Chris@127: */ Chris@127: Chris@127: class View : public QFrame, Chris@127: public XmlExportable Chris@127: { Chris@127: Q_OBJECT Chris@127: Chris@127: public: Chris@127: /** Chris@127: * Deleting a View does not delete any of its layers. They should Chris@127: * be managed elsewhere (e.g. by the Document). Chris@127: */ Chris@127: virtual ~View(); Chris@127: Chris@127: /** Chris@127: * Retrieve the first visible sample frame on the widget. Chris@127: * This is a calculated value based on the centre-frame, widget Chris@127: * width and zoom level. The result may be negative. Chris@127: */ Chris@127: virtual long getStartFrame() const; Chris@127: Chris@127: /** Chris@127: * Set the widget pan based on the given first visible frame. The Chris@127: * frame value may be negative. Chris@127: */ Chris@127: virtual void setStartFrame(long); Chris@127: Chris@127: /** Chris@127: * Return the centre frame of the visible widget. This is an Chris@127: * exact value that does not depend on the zoom block size. Other Chris@127: * frame values (start, end) are calculated from this based on the Chris@127: * zoom and other factors. Chris@127: */ Chris@127: virtual size_t getCentreFrame() const { return m_centreFrame; } Chris@127: Chris@127: /** Chris@127: * Set the centre frame of the visible widget. Chris@127: */ Chris@127: virtual void setCentreFrame(size_t f) { setCentreFrame(f, true); } Chris@127: Chris@127: /** Chris@127: * Retrieve the last visible sample frame on the widget. Chris@127: * This is a calculated value based on the centre-frame, widget Chris@127: * width and zoom level. Chris@127: */ Chris@127: virtual size_t getEndFrame() const; Chris@127: Chris@127: /** Chris@127: * Return the pixel x-coordinate corresponding to a given sample Chris@127: * frame (which may be negative). Chris@127: */ Chris@127: int getXForFrame(long frame) const; Chris@127: Chris@127: /** Chris@127: * Return the closest frame to the given pixel x-coordinate. Chris@127: */ Chris@127: long getFrameForX(int x) const; Chris@127: Chris@127: /** Chris@127: * Return the pixel y-coordinate corresponding to a given Chris@127: * frequency, if the frequency range is as specified. This does Chris@127: * not imply any policy about layer frequency ranges, but it might Chris@127: * be useful for layers to match theirs up if desired. Chris@127: * Chris@127: * Not thread-safe in logarithmic mode. Call only from GUI thread. Chris@127: */ Chris@127: float getYForFrequency(float frequency, float minFreq, float maxFreq, Chris@127: bool logarithmic) const; Chris@127: Chris@127: /** Chris@127: * Return the closest frequency to the given pixel y-coordinate, Chris@127: * if the frequency range is as specified. Chris@127: * Chris@127: * Not thread-safe in logarithmic mode. Call only from GUI thread. Chris@127: */ Chris@127: float getFrequencyForY(int y, float minFreq, float maxFreq, Chris@127: bool logarithmic) const; Chris@127: Chris@127: /** Chris@127: * Return the zoom level, i.e. the number of frames per pixel Chris@127: */ Chris@127: int getZoomLevel() const; Chris@127: Chris@127: /** Chris@127: * Set the zoom level, i.e. the number of frames per pixel. The Chris@127: * centre frame will be unchanged; the start and end frames will Chris@127: * change. Chris@127: */ Chris@127: virtual void setZoomLevel(size_t z); Chris@127: Chris@127: /** Chris@127: * Zoom in or out. Chris@127: */ Chris@127: virtual void zoom(bool in); Chris@127: Chris@127: /** Chris@127: * Scroll left or right by a smallish or largish amount. Chris@127: */ Chris@127: virtual void scroll(bool right, bool lots); Chris@127: Chris@127: virtual void addLayer(Layer *v); Chris@127: virtual void removeLayer(Layer *v); // does not delete the layer Chris@127: virtual int getLayerCount() const { return m_layers.size(); } Chris@127: Chris@127: /** Chris@127: * Return a layer, counted in stacking order. That is, layer 0 is Chris@127: * the bottom layer and layer "getLayerCount()-1" is the top one. Chris@127: */ Chris@277: virtual Layer *getLayer(int n) { Chris@285: if (n < int(m_layers.size())) return m_layers[n]; else return 0; Chris@277: } Chris@127: Chris@127: /** Chris@268: * Return the top layer. This is the same as Chris@268: * getLayer(getLayerCount()-1) if there is at least one layer, and Chris@268: * 0 otherwise. Chris@268: */ Chris@268: virtual Layer *getTopLayer() { Chris@268: return m_layers.empty() ? 0 : m_layers[m_layers.size()-1]; Chris@268: } Chris@268: Chris@268: /** Chris@127: * Return the layer last selected by the user. This is normally Chris@127: * the top layer, the same as getLayer(getLayerCount()-1). Chris@127: * However, if the user has selected the pane itself more recently Chris@127: * than any of the layers on it, this function will return 0. It Chris@127: * will also return 0 if there are no layers. Chris@127: */ Chris@127: virtual Layer *getSelectedLayer(); Chris@127: virtual const Layer *getSelectedLayer() const; Chris@127: Chris@127: virtual void setViewManager(ViewManager *m); Chris@127: virtual ViewManager *getViewManager() const { return m_manager; } Chris@127: Chris@127: virtual void setFollowGlobalPan(bool f); Chris@127: virtual bool getFollowGlobalPan() const { return m_followPan; } Chris@127: Chris@127: virtual void setFollowGlobalZoom(bool f); Chris@127: virtual bool getFollowGlobalZoom() const { return m_followZoom; } Chris@127: Chris@224: virtual bool hasLightBackground() const; Chris@287: virtual QColor getForeground() const; Chris@287: virtual QColor getBackground() const; Chris@127: Chris@127: enum TextStyle { Chris@127: BoxedText, Chris@127: OutlinedText Chris@127: }; Chris@127: Chris@127: virtual void drawVisibleText(QPainter &p, int x, int y, Chris@267: QString text, TextStyle style) const; Chris@267: Chris@270: virtual void drawMeasurementRect(QPainter &p, const Layer *, Chris@270: QRect rect, bool focus) const; Chris@127: Chris@127: virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const { Chris@127: return false; Chris@127: } Chris@127: virtual bool shouldIlluminateLocalSelection(QPoint &, bool &, bool &) const { Chris@127: return false; Chris@127: } Chris@127: Chris@127: virtual void setPlaybackFollow(PlaybackFollowMode m); Chris@127: virtual PlaybackFollowMode getPlaybackFollow() const { return m_followPlay; } Chris@127: Chris@127: typedef PropertyContainer::PropertyName PropertyName; Chris@127: Chris@127: // We implement the PropertyContainer API, although we don't Chris@127: // actually subclass PropertyContainer. We have our own Chris@127: // PropertyContainer that we can return on request that just Chris@127: // delegates back to us. Chris@127: virtual PropertyContainer::PropertyList getProperties() const; Chris@127: virtual QString getPropertyLabel(const PropertyName &) const; Chris@127: virtual PropertyContainer::PropertyType getPropertyType(const PropertyName &) const; Chris@127: virtual int getPropertyRangeAndValue(const PropertyName &, Chris@216: int *min, int *max, int *deflt) const; Chris@127: virtual QString getPropertyValueLabel(const PropertyName &, Chris@127: int value) const; Chris@127: virtual void setProperty(const PropertyName &, int value); Chris@127: virtual QString getPropertyContainerName() const { Chris@127: return objectName(); Chris@127: } Chris@127: virtual QString getPropertyContainerIconName() const = 0; Chris@127: Chris@127: virtual size_t getPropertyContainerCount() const; Chris@127: Chris@127: virtual const PropertyContainer *getPropertyContainer(size_t i) const; Chris@127: virtual PropertyContainer *getPropertyContainer(size_t i); Chris@127: Chris@229: // Render the contents on a wide canvas Chris@229: virtual QImage *toNewImage(size_t f0, size_t f1); Chris@226: virtual QImage *toNewImage(); Chris@229: virtual QSize getImageSize(size_t f0, size_t f1); Chris@229: virtual QSize getImageSize(); Chris@226: Chris@127: virtual int getTextLabelHeight(const Layer *layer, QPainter &) const; Chris@127: Chris@127: virtual bool getValueExtents(QString unit, float &min, float &max, Chris@127: bool &log) const; Chris@127: Chris@127: virtual QString toXmlString(QString indent = "", Chris@127: QString extraAttributes = "") const; Chris@127: Chris@222: // First frame actually in model, to right of scale, if present Chris@222: virtual size_t getFirstVisibleFrame() const; Chris@222: virtual size_t getLastVisibleFrame() const; Chris@222: Chris@127: size_t getModelsStartFrame() const; Chris@127: size_t getModelsEndFrame() const; Chris@127: Chris@127: signals: Chris@127: void propertyContainerAdded(PropertyContainer *pc); Chris@127: void propertyContainerRemoved(PropertyContainer *pc); Chris@127: void propertyContainerPropertyChanged(PropertyContainer *pc); Chris@197: void propertyContainerPropertyRangeChanged(PropertyContainer *pc); Chris@127: void propertyContainerNameChanged(PropertyContainer *pc); Chris@298: void propertyContainerSelected(PropertyContainer *pc); Chris@127: void propertyChanged(PropertyContainer::PropertyName); Chris@127: Chris@211: void centreFrameChanged(unsigned long frame, Chris@211: bool globalScroll, Chris@211: PlaybackFollowMode followMode); Chris@211: Chris@222: void zoomLevelChanged(unsigned long, bool); Chris@127: Chris@189: void contextHelpChanged(const QString &); Chris@189: Chris@127: public slots: Chris@127: virtual void modelChanged(); Chris@127: virtual void modelChanged(size_t startFrame, size_t endFrame); Chris@127: virtual void modelCompletionChanged(); Chris@127: virtual void modelReplaced(); Chris@127: virtual void layerParametersChanged(); Chris@197: virtual void layerParameterRangesChanged(); Chris@268: virtual void layerMeasurementRectsChanged(); Chris@127: virtual void layerNameChanged(); Chris@127: Chris@211: virtual void globalCentreFrameChanged(unsigned long); Chris@211: virtual void viewCentreFrameChanged(View *, unsigned long); Chris@127: virtual void viewManagerPlaybackFrameChanged(unsigned long); Chris@222: virtual void viewZoomLevelChanged(View *, unsigned long, bool); Chris@127: Chris@127: virtual void propertyContainerSelected(View *, PropertyContainer *pc); Chris@127: Chris@127: virtual void selectionChanged(); Chris@127: virtual void toolModeChanged(); Chris@133: virtual void overlayModeChanged(); Chris@133: virtual void zoomWheelsEnabledChanged(); Chris@127: Chris@127: protected: Chris@127: View(QWidget *, bool showProgress); Chris@127: virtual void paintEvent(QPaintEvent *e); Chris@127: virtual void drawSelections(QPainter &); Chris@127: virtual bool shouldLabelSelections() const { return true; } Chris@229: virtual bool render(QPainter &paint, int x0, size_t f0, size_t f1); Chris@127: Chris@127: typedef std::vector LayerList; Chris@127: Chris@127: int getModelsSampleRate() const; Chris@127: bool areLayersScrollable() const; Chris@127: LayerList getScrollableBackLayers(bool testChanged, bool &changed) const; Chris@127: LayerList getNonScrollableFrontLayers(bool testChanged, bool &changed) const; Chris@127: size_t getZoomConstraintBlockSize(size_t blockSize, Chris@127: ZoomConstraint::RoundingDirection dir = Chris@127: ZoomConstraint::RoundNearest) const; Chris@127: Chris@183: // True if the top layer(s) use colours for meaningful things. If Chris@183: // this is the case, selections will be shown using unfilled boxes Chris@183: // rather than with a translucent fill. Chris@183: bool areLayerColoursSignificant() const; Chris@183: Chris@217: // True if the top layer has a time axis on the x coordinate (this Chris@217: // is generally the case except for spectrum/slice layers). It Chris@217: // will not be possible to make or display selections if this is Chris@217: // false. Chris@217: bool hasTopLayerTimeXAxis() const; Chris@217: Chris@127: bool setCentreFrame(size_t f, bool doEmit); Chris@127: Chris@127: void checkProgress(void *object); Chris@127: Chris@127: size_t m_centreFrame; Chris@127: int m_zoomLevel; Chris@127: bool m_followPan; Chris@127: bool m_followZoom; Chris@127: PlaybackFollowMode m_followPlay; Chris@127: size_t m_playPointerFrame; Chris@127: bool m_lightBackground; Chris@127: bool m_showProgress; Chris@127: Chris@127: QPixmap *m_cache; Chris@127: size_t m_cacheCentreFrame; Chris@127: int m_cacheZoomLevel; Chris@127: bool m_selectionCached; Chris@127: Chris@127: bool m_deleting; Chris@127: Chris@127: LayerList m_layers; // I don't own these, but see dtor note above Chris@127: bool m_haveSelectedLayer; Chris@127: Chris@127: // caches for use in getScrollableBackLayers, getNonScrollableFrontLayers Chris@127: mutable LayerList m_lastScrollableBackLayers; Chris@127: mutable LayerList m_lastNonScrollableBackLayers; Chris@127: Chris@127: class LayerProgressBar : public QProgressBar { Chris@127: public: Chris@127: LayerProgressBar(QWidget *parent); Chris@127: virtual QString text() const { return m_text; } Chris@127: virtual void setText(QString text) { m_text = text; } Chris@127: protected: Chris@127: QString m_text; Chris@127: }; Chris@127: Chris@127: typedef std::map ProgressMap; Chris@127: ProgressMap m_progressBars; // I own the ProgressBars Chris@127: Chris@127: ViewManager *m_manager; // I don't own this Chris@127: ViewPropertyContainer *m_propertyContainer; // I own this Chris@127: }; Chris@127: Chris@127: Chris@127: // Use this for delegation, because we can't subclass from Chris@127: // PropertyContainer (which is a QObject) ourselves because of Chris@127: // ambiguity with QFrame parent Chris@127: Chris@127: class ViewPropertyContainer : public PropertyContainer Chris@127: { Chris@127: Q_OBJECT Chris@127: Chris@127: public: Chris@127: ViewPropertyContainer(View *v); Chris@127: PropertyList getProperties() const { return m_v->getProperties(); } Chris@127: QString getPropertyLabel(const PropertyName &n) const { Chris@127: return m_v->getPropertyLabel(n); Chris@127: } Chris@127: PropertyType getPropertyType(const PropertyName &n) const { Chris@127: return m_v->getPropertyType(n); Chris@127: } Chris@216: int getPropertyRangeAndValue(const PropertyName &n, int *min, int *max, Chris@216: int *deflt) const { Chris@216: return m_v->getPropertyRangeAndValue(n, min, max, deflt); Chris@127: } Chris@127: QString getPropertyValueLabel(const PropertyName &n, int value) const { Chris@127: return m_v->getPropertyValueLabel(n, value); Chris@127: } Chris@127: QString getPropertyContainerName() const { Chris@127: return m_v->getPropertyContainerName(); Chris@127: } Chris@127: QString getPropertyContainerIconName() const { Chris@127: return m_v->getPropertyContainerIconName(); Chris@127: } Chris@127: Chris@127: public slots: Chris@127: virtual void setProperty(const PropertyName &n, int value) { Chris@127: m_v->setProperty(n, value); Chris@127: } Chris@127: Chris@127: protected: Chris@127: View *m_v; Chris@127: }; Chris@127: Chris@127: #endif Chris@127: