view base/Layer.h @ 76:af2725b5d6fe

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 47fd14e29813
children c983dda79f72
line wrap: on
line source

/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    Sonic Visualiser
    An audio file viewer and annotation editor.
    Centre for Digital Music, Queen Mary, University of London.
    This file copyright 2006 Chris Cannam.
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#ifndef _LAYER_H_
#define _LAYER_H_

#include "PropertyContainer.h"
#include "XmlExportable.h"
#include "Selection.h"

#include <QObject>
#include <QRect>
#include <QXmlAttributes>

#include <map>

class ZoomConstraint;
class Model;
class QPainter;
class View;
class QMouseEvent;
class Clipboard;

/**
 * The base class for visual representations of the data found in a
 * Model.  Layers are expected to be able to draw themselves onto a
 * View, and may also be editable.
 */

class Layer : public PropertyContainer,
	      public XmlExportable
{
    Q_OBJECT

public:
    Layer();
    virtual ~Layer();

    virtual const Model *getModel() const = 0;
    virtual Model *getModel() {
	return const_cast<Model *>(const_cast<const Layer *>(this)->getModel());
    }

    virtual const ZoomConstraint *getZoomConstraint() const { return 0; }
    virtual void paint(View *, QPainter &, QRect) const = 0;   

    enum VerticalPosition {
	PositionTop, PositionMiddle, PositionBottom
    };
    virtual VerticalPosition getPreferredTimeRulerPosition() const {
	return PositionMiddle;
    }
    virtual VerticalPosition getPreferredFrameCountPosition() const {
	return PositionBottom;
    }

    virtual QString getPropertyContainerIconName() const;

    virtual QString getPropertyContainerName() const {
	return objectName();
    }

    virtual QString getLayerPresentationName() const;

    virtual int getVerticalScaleWidth(View *, QPainter &) const { return 0; }
    virtual void paintVerticalScale(View *, QPainter &, QRect) const { }

    virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos,
                                     std::vector<QRect> &) const { return false; }
    virtual void paintCrosshairs(View *, QPainter &, QPoint) const { }

    virtual QString getFeatureDescription(View *, QPoint &) const {
	return "";
    }

    enum SnapType {
	SnapLeft,
	SnapRight,
	SnapNearest,
	SnapNeighbouring
    };

    /**
     * Adjust the given frame to snap to the nearest feature, if
     * possible.
     *
     * If snap is SnapLeft or SnapRight, adjust the frame to match
     * that of the nearest feature in the given direction regardless
     * of how far away it is.  If snap is SnapNearest, adjust the
     * frame to that of the nearest feature in either direction.  If
     * snap is SnapNeighbouring, adjust the frame to that of the
     * nearest feature if it is close, and leave it alone (returning
     * false) otherwise.  SnapNeighbouring should always choose the
     * same feature that would be used in an editing operation through
     * calls to editStart etc.
     *
     * Return true if a suitable feature was found and frame adjusted
     * accordingly.  Return false if no suitable feature was
     * available.  Also return the resolution of the model in this
     * layer in sample frames.
     */
    virtual bool snapToFeatureFrame(View *v,
				    int &frame,
				    size_t &resolution,
				    SnapType snap) const {
	resolution = 1;
	return false;
    }

    // Draw and edit modes:
    //
    // Layer needs to get actual mouse events, I guess.  Draw mode is
    // probably the easier.

    virtual void drawStart(View *, QMouseEvent *) { }
    virtual void drawDrag(View *, QMouseEvent *) { }
    virtual void drawEnd(View *, QMouseEvent *) { }

    virtual void editStart(View *, QMouseEvent *) { }
    virtual void editDrag(View *, QMouseEvent *) { }
    virtual void editEnd(View *, QMouseEvent *) { }

    virtual void editOpen(View *, QMouseEvent *) { } // on double-click

    virtual void moveSelection(Selection s, size_t newStartFrame) { }
    virtual void resizeSelection(Selection s, Selection newSize) { }
    virtual void deleteSelection(Selection s) { }

    virtual void copy(Selection s, Clipboard &to) { }
    virtual void paste(const Clipboard &from, int frameOffset) { }

    // Text mode:
    //
    // Label nearest feature.  We need to get the feature coordinates
    // and current label from the layer, and then the pane can pop up
    // a little text entry dialog at the right location.  Or we edit
    // in place?  Probably the dialog is easier.

    /**
     * This should return true if the layer can safely be scrolled
     * automatically by a given view (simply copying the existing data
     * and then refreshing the exposed area) without altering its
     * meaning.  For the view widget as a whole this is usually not
     * possible because of invariant (non-scrolling) material
     * displayed over the top, but the widget may be able to optimise
     * scrolling better if it is known that individual views can be
     * scrolled safely in this way.
     */
    virtual bool isLayerScrollable(const View *) const { return true; }

    /**
     * This should return true if the layer completely obscures any
     * underlying layers.  It's used to determine whether the view can
     * safely draw any selection rectangles under the layer instead of
     * over it, in the case where the layer is not scrollable and
     * therefore needs to be redrawn each time (so that the selection
     * rectangle can be cached).
     */
    virtual bool isLayerOpaque() const { return false; }

    /**
     * This should return true if the layer can be edited by the user.
     * If this is the case, the appropriate edit tools may be made
     * available by the application and the layer's drawStart/Drag/End
     * and editStart/Drag/End methods should be implemented.
     */
    virtual bool isLayerEditable() const { return false; }

    /**
     * Return the proportion of background work complete in drawing
     * this view, as a percentage -- in most cases this will be the
     * value returned by pointer from a call to the underlying model's
     * isReady(int *) call.  The widget may choose to show a progress
     * meter if it finds that this returns < 100 at any given moment.
     */
    virtual int getCompletion() const { return 100; }

    virtual void setObjectName(const QString &name);

    /**
     * Convert the layer's data (though not those of the model it
     * refers to) into an XML string for file output.  This class
     * implements the basic name/type/model-id output; subclasses will
     * typically call this superclass implementation with extra
     * attributes describing their particular properties.
     */
    virtual QString toXmlString(QString indent = "",
				QString extraAttributes = "") const;

    /**
     * Set the particular properties of a layer (those specific to the
     * subclass) from a set of XML attributes.  This is the effective
     * inverse of the toXmlString method.
     */
    virtual void setProperties(const QXmlAttributes &) = 0;

    /**
     * Indicate that a layer is not currently visible in the given
     * view and is not expected to become visible in the near future
     * (for example because the user has explicitly removed or hidden
     * it).  The layer may respond by (for example) freeing any cache
     * memory it is using, until next time its paint method is called,
     * when it should set itself un-dormant again.
     */
    virtual void setLayerDormant(const View *v, bool dormant) {
	m_dormancy[v] = dormant;
    }

    /**
     * Return whether the layer is dormant (i.e. hidden) in the given
     * view.
     */
    virtual bool isLayerDormant(const View *v) const {
	if (m_dormancy.find(v) == m_dormancy.end()) return false;
	return m_dormancy.find(v)->second;
    }

    virtual PlayParameters *getPlayParameters();

public slots:
    void showLayer(View *, bool show);

signals:
    void modelChanged();
    void modelCompletionChanged();
    void modelChanged(size_t startFrame, size_t endFrame);
    void modelReplaced();

    void layerParametersChanged();
    void layerNameChanged();

protected:
    mutable std::map<const void *, bool> m_dormancy;
};

#endif