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 <QFrame>
Chris@127: #include <QProgressBar>
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 <map>
Chris@315: #include <set>
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@313:     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@313:     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@313:     size_t getCentreFrame() const { return m_centreFrame; }
Chris@127: 
Chris@127:     /**
Chris@127:      * Set the centre frame of the visible widget.
Chris@127:      */
Chris@313:     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@313:     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@510:     virtual void scroll(bool right, bool lots, bool doEmit = true);
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@516:     virtual void setViewManager(ViewManager *m, long initialFrame);
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@630: 	OutlinedText,
Chris@630:         OutlinedItalicText
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@316:     virtual void toXml(QTextStream &stream, QString indent = "",
Chris@316:                        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@315:     typedef std::set<Model *> ModelSet;
Chris@315:     ModelSet getModels();
Chris@315: 
Chris@301:     //!!!
Chris@320:     Model *getAligningModel() const;
Chris@320:     size_t alignFromReference(size_t) const;
Chris@320:     size_t alignToReference(size_t) const;
Chris@301:     int getAlignedPlaybackFrame() const;
Chris@301: 
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@336:     void layerModelChanged();
Chris@336: 
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@320:     virtual void modelAlignmentCompletionChanged();
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@555:     virtual void progressCheckStalledTimerElapsed();
Chris@555: 
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@339:     virtual void setPaintFont(QPainter &paint);
Chris@339:     
Chris@127:     typedef std::vector<Layer *> 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@511:     void movePlayPointer(unsigned long f);
Chris@511: 
Chris@127:     void checkProgress(void *object);
Chris@384:     int getProgressBarWidth() const; // if visible
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@583:     QString             m_lastError;
Chris@583: 
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@555:     struct ProgressBarRec {
Chris@555:         QProgressBar *bar;
Chris@555:         int lastCheck;
Chris@555:         QTimer *checkTimer;
Chris@555:     };
Chris@555:     typedef std::map<Layer *, ProgressBarRec> 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: