| Chris@0 | 1 | 
| Chris@0 | 2 /* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 3 | 
| Chris@0 | 4 /* | 
| Chris@0 | 5     A waveform viewer and audio annotation editor. | 
| Chris@2 | 6     Chris Cannam, Queen Mary University of London, 2005-2006 | 
| Chris@0 | 7 | 
| Chris@0 | 8     This is experimental software.  Not for distribution. | 
| Chris@0 | 9 */ | 
| Chris@0 | 10 | 
| Chris@26 | 11 #ifndef _LAYER_H_ | 
| Chris@26 | 12 #define _LAYER_H_ | 
| Chris@0 | 13 | 
| Chris@0 | 14 #include "PropertyContainer.h" | 
| Chris@3 | 15 #include "XmlExportable.h" | 
| Chris@35 | 16 #include "Selection.h" | 
| Chris@0 | 17 | 
| Chris@0 | 18 #include <QObject> | 
| Chris@0 | 19 #include <QRect> | 
| Chris@6 | 20 #include <QXmlAttributes> | 
| Chris@0 | 21 | 
| Chris@39 | 22 #include <map> | 
| Chris@39 | 23 | 
| Chris@0 | 24 class ZoomConstraint; | 
| Chris@0 | 25 class Model; | 
| Chris@0 | 26 class QPainter; | 
| Chris@0 | 27 class View; | 
| Chris@12 | 28 class QMouseEvent; | 
| Chris@0 | 29 | 
| Chris@0 | 30 /** | 
| Chris@0 | 31  * The base class for visual representations of the data found in a | 
| Chris@0 | 32  * Model.  Layers are expected to be able to draw themselves onto a | 
| Chris@0 | 33  * View, and may also be editable. | 
| Chris@0 | 34  */ | 
| Chris@0 | 35 | 
| Chris@29 | 36 class Layer : public PropertyContainer, | 
| Chris@3 | 37 	      public XmlExportable | 
| Chris@0 | 38 { | 
| Chris@0 | 39     Q_OBJECT | 
| Chris@0 | 40 | 
| Chris@0 | 41 public: | 
| Chris@36 | 42     Layer(); | 
| Chris@0 | 43     virtual ~Layer(); | 
| Chris@0 | 44 | 
| Chris@0 | 45     virtual const Model *getModel() const = 0; | 
| Chris@0 | 46     virtual const ZoomConstraint *getZoomConstraint() const { return 0; } | 
| Chris@36 | 47     virtual void paint(View *, QPainter &, QRect) const = 0; | 
| Chris@0 | 48 | 
| Chris@0 | 49     enum VerticalPosition { | 
| Chris@0 | 50 	PositionTop, PositionMiddle, PositionBottom | 
| Chris@0 | 51     }; | 
| Chris@0 | 52     virtual VerticalPosition getPreferredTimeRulerPosition() const { | 
| Chris@0 | 53 	return PositionMiddle; | 
| Chris@0 | 54     } | 
| Chris@0 | 55     virtual VerticalPosition getPreferredFrameCountPosition() const { | 
| Chris@0 | 56 	return PositionBottom; | 
| Chris@0 | 57     } | 
| Chris@0 | 58 | 
| Chris@12 | 59     virtual QString getPropertyContainerIconName() const; | 
| Chris@12 | 60 | 
| Chris@0 | 61     virtual QString getPropertyContainerName() const { | 
| Chris@0 | 62 	return objectName(); | 
| Chris@0 | 63     } | 
| Chris@0 | 64 | 
| Chris@36 | 65     virtual int getVerticalScaleWidth(View *, QPainter &) const { return 0; } | 
| Chris@36 | 66     virtual void paintVerticalScale(View *, QPainter &, QRect) const { } | 
| Chris@0 | 67 | 
| Chris@36 | 68     virtual QString getFeatureDescription(View *, QPoint &) const { | 
| Chris@20 | 69 	return ""; | 
| Chris@0 | 70     } | 
| Chris@0 | 71 | 
| Chris@8 | 72     //!!! We also need a method (like the vertical scale method) for | 
| Chris@8 | 73     //drawing additional scales like a colour scale.  That is, unless | 
| Chris@8 | 74     //all applicable layers can actually do this from | 
| Chris@8 | 75     //paintVerticalScale as well? | 
| Chris@8 | 76 | 
| Chris@23 | 77     enum SnapType { | 
| Chris@23 | 78 	SnapLeft, | 
| Chris@23 | 79 	SnapRight, | 
| Chris@23 | 80 	SnapNearest, | 
| Chris@23 | 81 	SnapNeighbouring | 
| Chris@23 | 82     }; | 
| Chris@8 | 83 | 
| Chris@23 | 84     /** | 
| Chris@23 | 85      * Adjust the given frame to snap to the nearest feature, if | 
| Chris@23 | 86      * possible. | 
| Chris@23 | 87      * | 
| Chris@23 | 88      * If snap is SnapLeft or SnapRight, adjust the frame to match | 
| Chris@23 | 89      * that of the nearest feature in the given direction regardless | 
| Chris@23 | 90      * of how far away it is.  If snap is SnapNearest, adjust the | 
| Chris@23 | 91      * frame to that of the nearest feature in either direction.  If | 
| Chris@23 | 92      * snap is SnapNeighbouring, adjust the frame to that of the | 
| Chris@23 | 93      * nearest feature if it is close, and leave it alone (returning | 
| Chris@23 | 94      * false) otherwise.  SnapNeighbouring should always choose the | 
| Chris@23 | 95      * same feature that would be used in an editing operation through | 
| Chris@23 | 96      * calls to editStart etc. | 
| Chris@23 | 97      * | 
| Chris@23 | 98      * Return true if a suitable feature was found and frame adjusted | 
| Chris@23 | 99      * accordingly.  Return false if no suitable feature was | 
| Chris@23 | 100      * available.  Also return the resolution of the model in this | 
| Chris@23 | 101      * layer in sample frames. | 
| Chris@23 | 102      */ | 
| Chris@36 | 103     virtual bool snapToFeatureFrame(View *v, | 
| Chris@36 | 104 				    int &frame, | 
| Chris@23 | 105 				    size_t &resolution, | 
| Chris@23 | 106 				    SnapType snap) const { | 
| Chris@8 | 107 	resolution = 1; | 
| Chris@23 | 108 	return false; | 
| Chris@8 | 109     } | 
| Chris@8 | 110 | 
| Chris@12 | 111     // Draw and edit modes: | 
| Chris@8 | 112     // | 
| Chris@12 | 113     // Layer needs to get actual mouse events, I guess.  Draw mode is | 
| Chris@8 | 114     // probably the easier. | 
| Chris@8 | 115 | 
| Chris@36 | 116     virtual void drawStart(View *, QMouseEvent *) { } | 
| Chris@36 | 117     virtual void drawDrag(View *, QMouseEvent *) { } | 
| Chris@36 | 118     virtual void drawEnd(View *, QMouseEvent *) { } | 
| Chris@13 | 119 | 
| Chris@36 | 120     virtual void editStart(View *, QMouseEvent *) { } | 
| Chris@36 | 121     virtual void editDrag(View *, QMouseEvent *) { } | 
| Chris@36 | 122     virtual void editEnd(View *, QMouseEvent *) { } | 
| Chris@12 | 123 | 
| Chris@36 | 124     virtual void editOpen(View *, QMouseEvent *) { } // on double-click | 
| Chris@32 | 125 | 
| Chris@35 | 126     virtual void moveSelection(Selection s, size_t newStartFrame) { } | 
| Chris@35 | 127     virtual void resizeSelection(Selection s, Selection newSize) { } | 
| Chris@35 | 128     virtual void deleteSelection(Selection s) { } | 
| Chris@35 | 129 | 
| Chris@35 | 130 | 
| Chris@8 | 131     // Text mode: | 
| Chris@8 | 132     // | 
| Chris@8 | 133     // Label nearest feature.  We need to get the feature coordinates | 
| Chris@8 | 134     // and current label from the layer, and then the pane can pop up | 
| Chris@8 | 135     // a little text entry dialog at the right location.  Or we edit | 
| Chris@8 | 136     // in place?  Probably the dialog is easier. | 
| Chris@8 | 137 | 
| Chris@0 | 138     /** | 
| Chris@36 | 139      * This should return true if the layer can safely be scrolled | 
| Chris@36 | 140      * automatically by a given view (simply copying the existing data | 
| Chris@0 | 141      * and then refreshing the exposed area) without altering its | 
| Chris@36 | 142      * meaning.  For the view widget as a whole this is usually not | 
| Chris@0 | 143      * possible because of invariant (non-scrolling) material | 
| Chris@0 | 144      * displayed over the top, but the widget may be able to optimise | 
| Chris@0 | 145      * scrolling better if it is known that individual views can be | 
| Chris@0 | 146      * scrolled safely in this way. | 
| Chris@0 | 147      */ | 
| Chris@36 | 148     virtual bool isLayerScrollable(const View *) const { return true; } | 
| Chris@0 | 149 | 
| Chris@0 | 150     /** | 
| Chris@10 | 151      * This should return true if the layer completely obscures any | 
| Chris@10 | 152      * underlying layers.  It's used to determine whether the view can | 
| Chris@10 | 153      * safely draw any selection rectangles under the layer instead of | 
| Chris@10 | 154      * over it, in the case where the layer is not scrollable and | 
| Chris@10 | 155      * therefore needs to be redrawn each time (so that the selection | 
| Chris@10 | 156      * rectangle can be cached). | 
| Chris@10 | 157      */ | 
| Chris@10 | 158     virtual bool isLayerOpaque() const { return false; } | 
| Chris@10 | 159 | 
| Chris@10 | 160     /** | 
| Chris@18 | 161      * This should return true if the layer can be edited by the user. | 
| Chris@18 | 162      * If this is the case, the appropriate edit tools may be made | 
| Chris@18 | 163      * available by the application and the layer's drawStart/Drag/End | 
| Chris@18 | 164      * and editStart/Drag/End methods should be implemented. | 
| Chris@18 | 165      */ | 
| Chris@18 | 166     virtual bool isLayerEditable() const { return false; } | 
| Chris@18 | 167 | 
| Chris@18 | 168     /** | 
| Chris@0 | 169      * Return the proportion of background work complete in drawing | 
| Chris@0 | 170      * this view, as a percentage -- in most cases this will be the | 
| Chris@0 | 171      * value returned by pointer from a call to the underlying model's | 
| Chris@0 | 172      * isReady(int *) call.  The widget may choose to show a progress | 
| Chris@0 | 173      * meter if it finds that this returns < 100 at any given moment. | 
| Chris@0 | 174      */ | 
| Chris@0 | 175     virtual int getCompletion() const { return 100; } | 
| Chris@0 | 176 | 
| Chris@0 | 177     virtual void setObjectName(const QString &name); | 
| Chris@0 | 178 | 
| Chris@7 | 179     /** | 
| Chris@7 | 180      * Convert the layer's data (though not those of the model it | 
| Chris@7 | 181      * refers to) into an XML string for file output.  This class | 
| Chris@7 | 182      * implements the basic name/type/model-id output; subclasses will | 
| Chris@7 | 183      * typically call this superclass implementation with extra | 
| Chris@7 | 184      * attributes describing their particular properties. | 
| Chris@7 | 185      */ | 
| Chris@3 | 186     virtual QString toXmlString(QString indent = "", | 
| Chris@3 | 187 				QString extraAttributes = "") const; | 
| Chris@3 | 188 | 
| Chris@7 | 189     /** | 
| Chris@7 | 190      * Set the particular properties of a layer (those specific to the | 
| Chris@7 | 191      * subclass) from a set of XML attributes.  This is the effective | 
| Chris@7 | 192      * inverse of the toXmlString method. | 
| Chris@7 | 193      */ | 
| Chris@6 | 194     virtual void setProperties(const QXmlAttributes &) = 0; | 
| Chris@6 | 195 | 
| Chris@24 | 196     /** | 
| Chris@39 | 197      * Indicate that a layer is not currently visible in the given | 
| Chris@39 | 198      * view and is not expected to become visible in the near future | 
| Chris@39 | 199      * (for example because the user has explicitly removed or hidden | 
| Chris@39 | 200      * it).  The layer may respond by (for example) freeing any cache | 
| Chris@39 | 201      * memory it is using, until next time its paint method is called. | 
| Chris@39 | 202      * It does not need to remember not to draw itself; the view will | 
| Chris@39 | 203      * handle that. | 
| Chris@24 | 204      */ | 
| Chris@39 | 205     virtual void setLayerDormant(const View *v, bool dormant) { | 
| Chris@39 | 206 	m_dormancy[v] = dormant; | 
| Chris@39 | 207     } | 
| Chris@24 | 208 | 
| Chris@29 | 209     /** | 
| Chris@39 | 210      * Return whether the layer is dormant (i.e. hidden) in the given | 
| Chris@39 | 211      * view. | 
| Chris@29 | 212      */ | 
| Chris@39 | 213     virtual bool isLayerDormant(const View *v) const { | 
| Chris@39 | 214 	if (m_dormancy.find(v) == m_dormancy.end()) return false; | 
| Chris@39 | 215 	return m_dormancy.find(v)->second; | 
| Chris@39 | 216     } | 
| Chris@29 | 217 | 
| Chris@29 | 218     virtual PlayParameters *getPlayParameters(); | 
| Chris@29 | 219 | 
| Chris@29 | 220 public slots: | 
| Chris@36 | 221     void showLayer(View *, bool show); | 
| Chris@28 | 222 | 
| Chris@0 | 223 signals: | 
| Chris@0 | 224     void modelChanged(); | 
| Chris@0 | 225     void modelCompletionChanged(); | 
| Chris@0 | 226     void modelChanged(size_t startFrame, size_t endFrame); | 
| Chris@0 | 227     void modelReplaced(); | 
| Chris@0 | 228 | 
| Chris@0 | 229     void layerParametersChanged(); | 
| Chris@0 | 230     void layerNameChanged(); | 
| Chris@0 | 231 | 
| Chris@0 | 232 protected: | 
| Chris@39 | 233     std::map<const void *, bool> m_dormancy; | 
| Chris@0 | 234 }; | 
| Chris@0 | 235 | 
| Chris@0 | 236 #endif | 
| Chris@0 | 237 |